From 02a59e2f4074889500ea29130b82aa80ced44d5e Mon Sep 17 00:00:00 2001 From: suncairong163 <105478245+suncairong163@users.noreply.github.com> Date: Wed, 22 Feb 2023 14:02:16 +0800 Subject: [PATCH 01/12] 3.2 consumer proxy invocation handler (#11108) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add dubbo mvc feign client to support mvc remote call * add dubbo mvc dispatcher servlet * add dubbo mvc dispatcher servlet * add MvcDispatcherServlet unit test * add dubbo mvc feign client to support mvc remote call * add dubbo mvc dispatcher servlet * add Dubbo mvc Provider RPCInvocation create * mvc remove unused code * remove mvc unused test code * remove unused code * BodyProviderParamParser param type modify * modify pom dependency version * add dubbo-metadata-api dependency * 新增依赖Jakarta支持 tomcat10 * ApplicationModel load ParamParser * ParamParser header & param map convert * ParamParser header & param map convert * 1 增加mvc协议 2 删除多余的resource包 * 1 增加mvc协议 2 增加netter server for http * 重构 NettyHttpClient NettyHttpServer * add path parse util & requestTemplate * add mvc consumer http-connection * mvc change HttpConnectionPreBuildIntercept name and add SerializeBodyIntercept extend * 1. 迁移mvc代码到rest下,合并http统一解析逻辑 2. 删除多余新增的remoting 调用模块 * 解决报错 * 1 抽象http 调用 2 改造rest调用风格 * 修改方法命名 * 修改方法名 * 删除多余的clients * 还原restprotocol,逻辑迁移至新分支 3.2_refactor_rest_protocol * add provider PathMatcher * add ServiceRestMetadata PathMatcher * add ServiceRestMetadata PathMatcher * add jax_rs and mvc annotation class constants * refactor PathMatcher name * add annotation class * maintain rest metadata * maintain jax_rs and mvc annotation * maintain jax_rs and mvc annotation * remove mvc module * remove rest mvc code to code review * add springmvc & rest annotation api module * add springmvc & rest annotation api module * add pathMatcher equal test * remove mvc module * rest provider RPCInvocation build * RPCInvocation build add dubbo-metadata-api dependency * RestMethodMetadata add method to consumer service map * RestMethodMetadata add method to consumer service map * rest consumer HttpInvokeInvocationHandler * rest consumer HttpInvokeInvocationHandler * add dubbo-metadata-api dependency * dubbo-rpc pom fix * 移动模块 * 删除模块引用 * consumer metadata resolve * RestProtocol consumer * RestProtocol consumer * requestConvert adaptive * JSON serializeObject to outputStream * JsonUtilsTest serialize & unSerialize * ok http client * http client reconstruct * rest protocol BaseConvert * rest protocol ParamParser reconstruct * rest protocol addKeepAliveHeader * requestTemplate isBodyEmpty() * remove unused code * create ConsumerParseContext * body default byte[0] * rest http message decode * writeTextContent & writeJsonContent & writeFormContent * remove mvc & rest annotation module * remove provider code * fix some pr advice * Add ASF license header for newly created files * move RestClientFactory code to dubbo-remoting-http * exact request convert code to a factory class * modify request convert code * modify dubbo-dependencies-bom * modify dubbo-remoting-http pom * refactor spi resource file name * dubbo-metadata-api remove dubbo-configcenter-zookeeper dependency for cyclic reference * newly create file add ASF * fix compile error * Enhance Code * add rest protocol ConsumerParamParser * some fix * rest json content-type test * RestProtocol merge doRefer code to protocolBindingRefer * remove restInvoker * rest protocol add JsonFactory to support custom * rest protocol add JsonFactory to support custom * jaxrs rest ParamNoAnnotatedProcessor support fix * add rest http message encode * add rest http message encode * rest okHttpClient requestBody fix * rest jaxrs form content-type support * RestProtocol protocolBindingRefer reconstruct * RestProtocol protocolBindingRefer reconstruct object to invoker & Invocation * move RequestTemplate to remoting-http * rest unit test * add httpclient & url connection rest client * Enhance invocation * enhance * add ParameterTypesComparator for metadataMap * add ASF for new file * Fix conflict * Fix header * fix import * fix import * fix import * fix import * fix import * Fix json * revert some change * Remove jsonfactory * Remove jsonfactory * Simplify rest client * Fix protocol * fix codec * tmp disable test * rest metadata resolver add interface judge * add rest metadata resolve unit test & fix AbstractServiceRestMetadataResolver * org.apache.dubbo.metadata.rest.ParamType null exclude & add default accept header * RestProtocolTest refer add context path unit test * some fix * ADD TODO * RESOLVE HTTP client java.net.SocketException: socket closed * RESOLVE HTTP client java.net.SocketException: socket closed * add spring mvc rest protocol unit test * rest protocol http response code deal * rest protocol http response message * fix some review advice * fix some review advice * add rest metadata resolve unit test & fix AbstractServiceRestMetadataResolver * org.apache.dubbo.metadata.rest.ParamType null exclude & add default accept header * RestProtocolTest refer add context path unit test * some fix * fix some review advice * add spring mvc rest protocol unit test * rest protocol http response code deal * rest protocol http response message * URLConnectionRestClient getMessage * remove unused import * import fix * code merge * code merge * code merge * code merge * code merge * Fix conflicts * change restResult InputStream to bytes protect from fd leak * merge code * Fix import * Fix import * Fix uts * Remove unused code * Fix logger * Update okhttp version * Update okhttp version * rest protocol add AnotherUserRestService service ut * rest protocol add String & byteArray codec * Fix version * Fix uts * rest protocol add XMLCodec * stream release * ServiceRestMetadata port change int to Integer * service RestMetadata service map init * xml codec change for xxe * Fix import * code style * code style * Fix shade --------- Co-authored-by: suncr Co-authored-by: Liujunjie <42363259+LiujunjieALiling@users.noreply.github.com> Co-authored-by: junjie3.liu Co-authored-by: Albumen Kevin --- .../org/apache/dubbo/common/json/JSON.java | 1 - .../dubbo/common/json/impl/FastJson2Impl.java | 1 - .../dubbo/common/json/impl/FastJsonImpl.java | 1 + .../dubbo/common/json/impl/JacksonImpl.java | 3 + .../ServiceDefinitionBuilderTest.java | 4 +- dubbo-demo/dubbo-demo-interface/pom.xml | 14 + .../apache/dubbo/demo/RestDemoService.java | 54 +++- .../src/main/java/po/TestPO.java | 69 ++++ .../dubbo/demo/consumer/Application.java | 31 +- .../demo/provider/RestDemoServiceImpl.java | 37 +++ dubbo-dependencies-bom/pom.xml | 21 ++ dubbo-distribution/dubbo-all/pom.xml | 30 ++ dubbo-metadata/dubbo-metadata-api/pom.xml | 8 - .../metadata/AbstractServiceNameMapping.java | 1 - .../dubbo/metadata/MetadataConstants.java | 38 ++- .../metadata/ParameterTypesComparator.java | 49 +++ ...ractAnnotatedMethodParameterProcessor.java | 30 +- ...AbstractNoAnnotatedParameterProcessor.java | 68 ++++ .../AbstractServiceRestMetadataResolver.java | 113 +++++-- .../AnnotatedMethodParameterProcessor.java | 15 +- .../apache/dubbo/metadata/rest/ArgInfo.java | 151 +++++++++ .../metadata/rest/JAXRSClassConstants.java | 62 ++++ ...AnnotatedParameterRequestTagProcessor.java | 32 ++ .../apache/dubbo/metadata/rest/ParamType.java | 97 ++++++ .../dubbo/metadata/rest/PathMatcher.java | 146 +++++++++ .../apache/dubbo/metadata/rest/PathUtil.java | 157 +++++++++ .../dubbo/metadata/rest/RequestMetadata.java | 19 +- .../metadata/rest/RestMetadataConstants.java | 20 ++ .../metadata/rest/RestMethodMetadata.java | 67 +++- .../metadata/rest/ServiceRestMetadata.java | 104 +++++- .../rest/ServiceRestMetadataResolver.java | 7 +- .../rest/SpringMvcClassConstants.java | 51 +++ .../rest/jaxrs/BodyParameterProcessor.java | 52 +++ .../jaxrs/DefaultValueParameterProcessor.java | 5 +- .../jaxrs/FormParamParameterProcessor.java | 2 +- .../jaxrs/HeaderParamParameterProcessor.java | 5 +- .../JAXRSServiceRestMetadataResolver.java | 12 + .../jaxrs/MatrixParamParameterProcessor.java | 2 +- .../ParamAnnotationParameterProcessor.java | 3 +- .../jaxrs/PathParamParameterProcessor.java | 37 +++ .../jaxrs/QueryParamParameterProcessor.java | 2 +- .../dubbo/metadata/rest/media/MediaType.java | 46 +++ .../FormBodyNoAnnotatedProcessor.java | 41 +++ .../JsonBodyNoAnnotatedProcessor.java | 36 +++ .../springmvc/ParamNoAnnotatedProcessor.java | 43 +++ .../PathVariableParameterProcessor.java | 43 +++ .../RequestBodyParameterProcessor.java | 46 +++ .../RequestHeaderParameterProcessor.java | 5 +- .../RequestParamParameterProcessor.java | 6 +- .../SpringMvcServiceRestMetadataResolver.java | 15 + .../dubbo/metadata/rest/tag/BodyTag.java | 20 ++ ...ata.rest.AnnotatedMethodParameterProcessor | 6 +- ...st.NoAnnotatedParameterRequestTagProcessor | 3 + .../dubbo/metadata/PathMatcherTest.java | 32 ++ .../metadata/rest/DefaultRestService.java | 15 + .../dubbo/metadata/rest/RestService.java | 4 + .../metadata/rest/SpringRestService.java | 26 +- .../metadata/rest/StandardRestService.java | 24 ++ .../rest/api/AnotherUserRestService.java | 45 +++ .../metadata/rest/api/JaxrsRestService.java | 65 ++++ .../rest/api/JaxrsRestServiceImpl.java | 50 +++ .../metadata/rest/api/SpringRestService.java | 52 +++ .../rest/api/SpringRestServiceImpl.java | 53 +++ .../JAXRSServiceRestMetadataResolverTest.java | 86 ++++- ...ingMvcServiceRestMetadataResolverTest.java | 67 +++- .../metrics/filter/MetricsFilterTest.java | 20 +- .../PrometheusMetricsReporterTest.java | 1 - .../command/DefaultCommandExecutorTest.java | 2 - .../MetadataServiceNameMappingTest.java | 2 - .../org/apache/dubbo/remoting/Constants.java | 14 +- dubbo-remoting/dubbo-remoting-http/pom.xml | 20 +- .../dubbo/remoting/http/BaseRestClient.java | 43 +++ .../dubbo/remoting/http/RequestTemplate.java | 303 ++++++++++++++++++ .../dubbo/remoting/http/RestClient.java | 50 +++ .../dubbo/remoting/http/RestResult.java | 35 ++ .../http/config/HttpClientConfig.java | 62 ++++ .../factory/AbstractHttpClientFactory.java | 87 +++++ .../http/factory/RestClientFactory.java | 38 +++ .../factory/impl/ApacheHttpClientFactory.java | 37 +++ .../factory/impl/OkHttpClientFactory.java | 36 +++ .../impl/URLConnectionClientFactory.java | 36 +++ .../http/restclient/HttpClientRestClient.java | 153 +++++++++ .../http/restclient/OKHttpRestClient.java | 144 +++++++++ .../restclient/URLConnectionRestClient.java | 181 +++++++++++ ...bo.remoting.http.factory.RestClientFactory | 4 + ...ultiplexProtocolConnectionManagerTest.java | 1 - .../rpc/protocol/AbstractProxyProtocol.java | 51 +-- .../org/apache/dubbo/rpc/RpcContextTest.java | 2 - dubbo-rpc/dubbo-rpc-rest/pom.xml | 40 ++- .../rest/NettyRestProtocolServer.java | 4 +- .../protocol/rest/ReferenceCountedClient.java | 19 +- .../dubbo/rpc/protocol/rest/RestProtocol.java | 264 +++++++-------- .../rpc/protocol/rest/RestServerFactory.java | 8 +- .../rest/annotation/BaseParseContext.java | 51 +++ .../protocol/rest/annotation/ParamParser.java | 24 ++ .../rest/annotation/ParamParserManager.java | 61 ++++ .../consumer/HttpConnectionConfig.java | 51 +++ .../consumer/HttpConnectionCreateContext.java | 81 +++++ .../HttpConnectionPreBuildIntercept.java | 25 ++ .../inercept/AddMustAttachmentIntercept.java | 48 +++ .../inercept/AttachmentIntercept.java | 70 ++++ .../inercept/ParamParseIntercept.java | 39 +++ .../inercept/PathVariableIntercept.java | 51 +++ .../inercept/RequestHeaderIntercept.java | 54 ++++ .../inercept/SerializeBodyIntercept.java | 66 ++++ .../annotation/metadata/MetadataResolver.java | 57 ++++ .../consumer/BaseConsumerParamParser.java | 29 ++ .../consumer/BodyConsumerParamParser.java | 45 +++ .../parse/consumer/ConsumerParseContext.java | 34 ++ .../consumer/FormConsumerParamParser.java | 54 ++++ .../consumer/HeaderConsumerParamParser.java | 41 +++ .../ParameterConsumerParamParser.java | 41 +++ .../protocol/rest/constans/RestConstant.java | 60 ++++ .../CodeStyleNotSupportException.java | 24 ++ .../rest/exception/HttpClientException.java | 24 ++ .../RemoteServerInternalException.java | 24 ++ .../UnSupportContentTypeException.java | 27 ++ .../rest/message/HttpMessageCodec.java | 30 ++ .../rest/message/HttpMessageCodecManager.java | 51 +++ .../rest/message/HttpMessageDecode.java | 24 ++ .../rest/message/HttpMessageEncode.java | 28 ++ .../rest/message/MediaTypeMatcher.java | 64 ++++ .../rest/message/codec/ByteArrayCodec.java | 45 +++ .../rest/message/codec/JsonCodec.java | 57 ++++ .../rest/message/codec/MultiValueCodec.java | 48 +++ .../rest/message/codec/StringCodec.java | 47 +++ .../rest/message/codec/TextCodec.java | 47 +++ .../protocol/rest/message/codec/XMLCodec.java | 72 +++++ .../protocol/rest/util/DataParseUtils.java | 215 +++++++++++++ .../rpc/protocol/rest/util/MediaTypeUtil.java | 42 +++ .../protocol/rest/util/MultiValueCreator.java | 55 ++++ .../rpc/protocol/rest/util/NumberUtils.java | 149 +++++++++ .../rpc/protocol/rest/util/ReflectUtils.java | 269 ++++++++++++++++ .../rpc/protocol/rest/util/StreamUtils.java | 41 +++ .../rpc/protocol/rest/util/TypeUtil.java | 38 +++ ...n.consumer.HttpConnectionPreBuildIntercept | 6 + ...ram.parse.consumer.BaseConsumerParamParser | 5 + ...rpc.protocol.rest.message.HttpMessageCodec | 6 + .../dubbo/rpc/protocol/rest/DemoService.java | 29 +- .../rpc/protocol/rest/DemoServiceImpl.java | 19 +- ...olTest.java => JaxrsRestProtocolTest.java} | 44 ++- .../rest/SpringMvcRestProtocolTest.java | 278 ++++++++++++++++ .../apache/dubbo/rpc/protocol/rest/User.java | 62 ++++ .../rpc/protocol/rest/mvc/DemoService.java | 36 +++ .../rest/mvc/SpringDemoServiceImpl.java | 55 ++++ .../rest/rest/AnotherUserRestService.java | 57 ++++ .../rest/rest/AnotherUserRestServiceImpl.java | 53 +++ .../rest/rest/RegistrationResult.java | 44 +++ .../protocol/rest/rest/RestDemoService.java | 43 +++ .../rest/rest/RestDemoServiceImpl.java | 55 ++++ .../test/common/api/SpringmvcDemoService.java | 21 ++ 151 files changed, 6947 insertions(+), 376 deletions(-) create mode 100644 dubbo-demo/dubbo-demo-interface/src/main/java/po/TestPO.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ParameterTypesComparator.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractNoAnnotatedParameterProcessor.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ArgInfo.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/JAXRSClassConstants.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/NoAnnotatedParameterRequestTagProcessor.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ParamType.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathMatcher.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathUtil.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/SpringMvcClassConstants.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/BodyParameterProcessor.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/PathParamParameterProcessor.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/media/MediaType.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/FormBodyNoAnnotatedProcessor.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/JsonBodyNoAnnotatedProcessor.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/ParamNoAnnotatedProcessor.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/PathVariableParameterProcessor.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/RequestBodyParameterProcessor.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/tag/BodyTag.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.NoAnnotatedParameterRequestTagProcessor create mode 100644 dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/PathMatcherTest.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/AnotherUserRestService.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestService.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestServiceImpl.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestService.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestServiceImpl.java create mode 100644 dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/BaseRestClient.java create mode 100644 dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RequestTemplate.java create mode 100644 dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestClient.java create mode 100644 dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestResult.java create mode 100644 dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/config/HttpClientConfig.java create mode 100644 dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/AbstractHttpClientFactory.java create mode 100644 dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/RestClientFactory.java create mode 100644 dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/ApacheHttpClientFactory.java create mode 100644 dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/OkHttpClientFactory.java create mode 100644 dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/URLConnectionClientFactory.java create mode 100644 dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java create mode 100644 dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/OKHttpRestClient.java create mode 100644 dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/URLConnectionRestClient.java create mode 100644 dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.factory.RestClientFactory create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/BaseParseContext.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParser.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParserManager.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionConfig.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionCreateContext.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionPreBuildIntercept.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AddMustAttachmentIntercept.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AttachmentIntercept.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/ParamParseIntercept.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/PathVariableIntercept.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/RequestHeaderIntercept.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/SerializeBodyIntercept.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/metadata/MetadataResolver.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BaseConsumerParamParser.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BodyConsumerParamParser.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ConsumerParseContext.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/FormConsumerParamParser.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/HeaderConsumerParamParser.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ParameterConsumerParamParser.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/constans/RestConstant.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/CodeStyleNotSupportException.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/HttpClientException.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RemoteServerInternalException.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/UnSupportContentTypeException.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodec.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodecManager.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageDecode.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageEncode.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/MediaTypeMatcher.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/ByteArrayCodec.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/JsonCodec.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/MultiValueCodec.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/StringCodec.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/TextCodec.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/XMLCodec.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/DataParseUtils.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MediaTypeUtil.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MultiValueCreator.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/NumberUtils.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/ReflectUtils.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/StreamUtils.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/TypeUtil.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BaseConsumerParamParser create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec rename dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/{RestProtocolTest.java => JaxrsRestProtocolTest.java} (85%) create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/SpringMvcRestProtocolTest.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/User.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/DemoService.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringDemoServiceImpl.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestService.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestServiceImpl.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RegistrationResult.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java create mode 100644 dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java create mode 100644 dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/SpringmvcDemoService.java diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/json/JSON.java b/dubbo-common/src/main/java/org/apache/dubbo/common/json/JSON.java index c4210c60cb3..7eddf3d988b 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/json/JSON.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/json/JSON.java @@ -48,5 +48,4 @@ public interface JSON { List> checkObjectList(List rawList); List checkStringList(List rawList); - } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/FastJson2Impl.java b/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/FastJson2Impl.java index a4021f059a6..88f8920e37c 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/FastJson2Impl.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/FastJson2Impl.java @@ -22,7 +22,6 @@ import java.util.List; public class FastJson2Impl extends AbstractJSONImpl { - @Override public T toJavaObject(String json, Type type) { return com.alibaba.fastjson2.JSON.parseObject(json, type); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/FastJsonImpl.java b/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/FastJsonImpl.java index 0bd3201ed73..9397183f61a 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/FastJsonImpl.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/FastJsonImpl.java @@ -37,4 +37,5 @@ public List toJavaList(String json, Class clazz) { public String toJson(Object obj) { return com.alibaba.fastjson.JSON.toJSONString(obj, SerializerFeature.DisableCircularReferenceDetect); } + } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/JacksonImpl.java b/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/JacksonImpl.java index 1e4387854e8..dc6f6e380bd 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/JacksonImpl.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/JacksonImpl.java @@ -16,8 +16,10 @@ */ package org.apache.dubbo.common.json.impl; + import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; @@ -25,6 +27,7 @@ import java.util.List; public class JacksonImpl extends AbstractJSONImpl { + private ObjectMapper objectMapper = new ObjectMapper(); private volatile Object jacksonCache = null; diff --git a/dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuilderTest.java b/dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuilderTest.java index 1df9ebdcdfb..1fe844ac2cf 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuilderTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuilderTest.java @@ -21,8 +21,6 @@ import org.apache.dubbo.metadata.definition.model.TypeDefinition; import org.apache.dubbo.metadata.definition.service.ComplexObject; import org.apache.dubbo.metadata.definition.service.DemoService; -import org.apache.dubbo.rpc.model.FrameworkModel; - import org.apache.dubbo.rpc.model.FrameworkModel; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -134,4 +132,4 @@ void checkComplexObjectAsParam(FullServiceDefinition fullServiceDefinition) { Assertions.assertEquals(Integer.class.getCanonicalName(), listTypeDefinition.getItems().get(0)); } -} \ No newline at end of file +} diff --git a/dubbo-demo/dubbo-demo-interface/pom.xml b/dubbo-demo/dubbo-demo-interface/pom.xml index b2b1cd73326..7cc0c57707f 100644 --- a/dubbo-demo/dubbo-demo-interface/pom.xml +++ b/dubbo-demo/dubbo-demo-interface/pom.xml @@ -35,5 +35,19 @@ org.apache.dubbo dubbo-rpc-rest + + + + org.springframework + spring-web + test + + + + + org.springframework + spring-context + test + diff --git a/dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/RestDemoService.java b/dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/RestDemoService.java index 308b061c49c..8caa8ccebcf 100644 --- a/dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/RestDemoService.java +++ b/dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/RestDemoService.java @@ -16,18 +16,26 @@ */ package org.apache.dubbo.demo; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; + +import po.TestPO; + import javax.ws.rs.Path; +import javax.ws.rs.GET; +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.POST; +import javax.ws.rs.FormParam; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; @Path("/demoService") public interface RestDemoService { @GET @Path("/hello") + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.TEXT_PLAIN) Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b); @GET @@ -42,4 +50,44 @@ public interface RestDemoService { @GET @Path("/getRemoteApplicationName") String getRemoteApplicationName(); + + @POST + @Path("/testBody1") + @Consumes({MediaType.TEXT_PLAIN}) + Integer testBody(Integer b); + + @POST + @Path("/testBody2") + @Consumes({MediaType.TEXT_PLAIN}) + String testBody2(String b); + + @POST + @Path("/testBody3") + @Consumes({MediaType.TEXT_PLAIN}) + Boolean testBody2(Boolean b); + + @POST + @Path("/testBody3") + @Consumes({MediaType.TEXT_PLAIN}) + TestPO testBody2(TestPO b); + + + @POST + @Path("/testBody5") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + TestPO testBody5(TestPO testPO); + + @POST + @Path("/testForm1") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.TEXT_PLAIN) + String testForm1(@FormParam("name") String test); + + + @POST + @Path("/testForm2") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.APPLICATION_FORM_URLENCODED) + MultivaluedMap testForm2(MultivaluedMap map); } diff --git a/dubbo-demo/dubbo-demo-interface/src/main/java/po/TestPO.java b/dubbo-demo/dubbo-demo-interface/src/main/java/po/TestPO.java new file mode 100644 index 00000000000..4cd1df96d8b --- /dev/null +++ b/dubbo-demo/dubbo-demo-interface/src/main/java/po/TestPO.java @@ -0,0 +1,69 @@ +/* + * 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 po; + +public class TestPO { + private String name; + private String address; + private int age; + + public TestPO(String name, String address, int age) { + this.name = name; + this.address = address; + this.age = age; + } + + public TestPO() { + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public static TestPO getInstance() { + return new TestPO("dubbo", "hangzhou", 10); + } + + @Override + public String toString() { + return "TestPO{" + + "name='" + name + '\'' + + ", address='" + address + '\'' + + ", age=" + age + + '}'; + } +} diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java index 01adf4a60df..b32502fcc3e 100644 --- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java +++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java @@ -22,7 +22,11 @@ import org.apache.dubbo.demo.TripleService; import org.springframework.context.support.ClassPathXmlApplicationContext; +import po.TestPO; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import java.util.Arrays; import java.util.concurrent.CompletableFuture; public class Application { @@ -56,23 +60,22 @@ public static void main(String[] args) throws Exception { new Thread(() -> { while (true) { try { - String restResult = restDemoService.sayHello("rest"); + Object restResult = restDemoService.sayHello("rest"); + System.out.println(restResult + " from separated thread."); + restResult = restDemoService.testBody5(TestPO.getInstance()); System.out.println(restResult + " from separated thread."); - } catch (Exception e) { - e.printStackTrace(); - } - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - } - } - }).start(); - new Thread(() -> { - while (true) { - try { - String restResult = tripleService.hello(); + restResult = restDemoService.hello(1, 2); System.out.println(restResult + " from separated thread."); + + String form1 = restDemoService.testForm1("form1"); + System.out.println(form1); + + MultivaluedHashMap multivaluedHashMap = new MultivaluedHashMap(); + multivaluedHashMap.put("1", Arrays.asList("1")); + multivaluedHashMap.put("2", Arrays.asList("2")); + MultivaluedMap form2 = restDemoService.testForm2(multivaluedHashMap); + System.out.println(form2); } catch (Exception e) { e.printStackTrace(); } diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/RestDemoServiceImpl.java b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/RestDemoServiceImpl.java index 67d523aee00..c0864c7a1bb 100644 --- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/RestDemoServiceImpl.java +++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/RestDemoServiceImpl.java @@ -19,7 +19,9 @@ import org.apache.dubbo.demo.RestDemoService; import org.apache.dubbo.rpc.RpcContext; +import po.TestPO; +import javax.ws.rs.core.MultivaluedMap; import java.util.Map; public class RestDemoServiceImpl implements RestDemoService { @@ -55,4 +57,39 @@ public static Map getAttachments() { public String getRemoteApplicationName() { return RpcContext.getServiceContext().getRemoteApplicationName(); } + + @Override + public Integer testBody(Integer b) { + return b; + } + + @Override + public String testBody2(String b) { + return b; + } + + @Override + public Boolean testBody2(Boolean b) { + return b; + } + + @Override + public TestPO testBody2(TestPO b) { + return b; + } + + @Override + public TestPO testBody5(TestPO testPO) { + return testPO; + } + + + public String testForm1(String test) { + return test; + } + + + public MultivaluedMap testForm2(MultivaluedMap map) { + return map; + } } diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml index ed95d089930..cc437deab39 100644 --- a/dubbo-dependencies-bom/pom.xml +++ b/dubbo-dependencies-bom/pom.xml @@ -117,6 +117,7 @@ 3.19.6 1.3.2 3.1.0 + 5.0.0 9.4.50.v20221201 3.0.1 1.1.0.Final @@ -139,6 +140,7 @@ 0.16.0 1.0.4 3.5.2 + 4.10.0 2.1.1 3.15.3.Final @@ -419,6 +421,25 @@ javax.servlet-api ${servlet_version} + + + + jakarta.servlet + jakarta.servlet-api + ${jakarta.servlet_version} + + + + com.squareup.okhttp3 + okhttp + ${okhttp_version} + + + com.squareup.okhttp3 + mockwebserver + ${okhttp_version} + + org.eclipse.jetty jetty-server diff --git a/dubbo-distribution/dubbo-all/pom.xml b/dubbo-distribution/dubbo-all/pom.xml index b9bb45e01a4..23b1192686c 100644 --- a/dubbo-distribution/dubbo-all/pom.xml +++ b/dubbo-distribution/dubbo-all/pom.xml @@ -906,6 +906,36 @@ META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner + + + META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.NoAnnotatedParameterRequestTagProcessor + + + + + META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec + + + + + META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept + + + + + META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BaseConsumerParamParser + + + + + META-INF/dubbo/internal/org.apache.dubbo.remoting.http.factory.RestClientFactory + + diff --git a/dubbo-metadata/dubbo-metadata-api/pom.xml b/dubbo-metadata/dubbo-metadata-api/pom.xml index 67e4dd8ff01..9fc83dd32f9 100644 --- a/dubbo-metadata/dubbo-metadata-api/pom.xml +++ b/dubbo-metadata/dubbo-metadata-api/pom.xml @@ -45,14 +45,6 @@ ${project.parent.version} - - - org.apache.dubbo - dubbo-configcenter-zookeeper - ${project.parent.version} - test - - javax.ws.rs diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractServiceNameMapping.java index e714b0d0039..675067276d3 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractServiceNameMapping.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractServiceNameMapping.java @@ -41,7 +41,6 @@ import static java.util.Collections.unmodifiableSet; import static java.util.stream.Collectors.toSet; import static java.util.stream.Stream.of; - import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_FAILED_LOAD_MAPPING_CACHE; import static org.apache.dubbo.common.constants.RegistryConstants.SUBSCRIBED_SERVICE_NAMES_KEY; import static org.apache.dubbo.common.utils.CollectionUtils.toTreeSet; diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java index c6bda320c68..3fe976a1de8 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java @@ -16,16 +16,30 @@ */ package org.apache.dubbo.metadata; -public class MetadataConstants { - public static final String KEY_SEPARATOR = ":"; - public static final String DEFAULT_PATH_TAG = "metadata"; - public static final String KEY_REVISON_PREFIX = "revision"; - public static final String META_DATA_STORE_TAG = ".metaData"; - public static final String SERVICE_META_DATA_STORE_TAG = ".smd"; - public static final String CONSUMER_META_DATA_STORE_TAG = ".cmd"; - public static final String METADATA_PUBLISH_DELAY_KEY = "dubbo.application.metadata.publish.delay"; - public static final int DEFAULT_METADATA_PUBLISH_DELAY = 1000; - public static final String METADATA_PROXY_TIMEOUT_KEY = "dubbo.application.metadata.proxy.delay"; - public static final int DEFAULT_METADATA_TIMEOUT_VALUE = 5000; - public static String REPORT_CONSUMER_URL_KEY = "report-consumer-definition"; +import static org.apache.dubbo.common.utils.ClassUtils.getClassLoader; +import static org.apache.dubbo.common.utils.ClassUtils.resolveClass; + +public interface MetadataConstants { + String KEY_SEPARATOR = ":"; + String DEFAULT_PATH_TAG = "metadata"; + String KEY_REVISON_PREFIX = "revision"; + String META_DATA_STORE_TAG = ".metaData"; + String SERVICE_META_DATA_STORE_TAG = ".smd"; + String CONSUMER_META_DATA_STORE_TAG = ".cmd"; + String METADATA_PUBLISH_DELAY_KEY = "dubbo.application.metadata.publish.delay"; + int DEFAULT_METADATA_PUBLISH_DELAY = 1000; + String METADATA_PROXY_TIMEOUT_KEY = "dubbo.application.metadata.proxy.delay"; + int DEFAULT_METADATA_TIMEOUT_VALUE = 5000; + String REPORT_CONSUMER_URL_KEY = "report-consumer-definition"; + String JAVAX_SERVLET_REQ_CLASS_NAME = "javax.servlet.ServletRequest"; + Class JAVAX_SERVLET_REQ_CLASS = resolveClass(JAVAX_SERVLET_REQ_CLASS_NAME, getClassLoader()); + String JAVAX_SERVLET_RES_CLASS_NAME = "javax.servlet.ServletResponse"; + Class JAVAX_SERVLET_RES_CLASS = resolveClass(JAVAX_SERVLET_RES_CLASS_NAME, getClassLoader()); + String JAKARTA_SERVLET_REQ_CLASS_NAME = "jakarta.servlet.ServletRequest"; + Class JAKARTA_SERVLET_REQ_CLASS = resolveClass(JAKARTA_SERVLET_REQ_CLASS_NAME, getClassLoader()); + String JAKARTA_SERVLET_RES_CLASS_NAME = "jakarta.servlet.ServletResponse"; + Class JAKARTA_SERVLET_RES_CLASS = resolveClass(JAKARTA_SERVLET_RES_CLASS_NAME, getClassLoader()); + String PATH_SEPARATOR = "/"; + String APPLICATION_FORM_URLENCODED_VALUE = "application/x-www-form-urlencoded"; + String APPLICATION_JSON_VALUE = "application/json"; } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ParameterTypesComparator.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ParameterTypesComparator.java new file mode 100644 index 00000000000..35527d0356c --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ParameterTypesComparator.java @@ -0,0 +1,49 @@ +/* + * 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.metadata; + +import java.util.Arrays; + +public class ParameterTypesComparator { + + private Class[] parameterTypes; + + + public ParameterTypesComparator(Class[] parameterTypes) { + this.parameterTypes = parameterTypes; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ParameterTypesComparator that = (ParameterTypesComparator) o; + return Arrays.equals(parameterTypes, that.parameterTypes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(parameterTypes); + } + + + public static ParameterTypesComparator getInstance(Class[] parameterTypes) { + return new ParameterTypesComparator(parameterTypes); + } + + +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractAnnotatedMethodParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractAnnotatedMethodParameterProcessor.java index a168f0f96bf..71d47cfda6c 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractAnnotatedMethodParameterProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractAnnotatedMethodParameterProcessor.java @@ -16,12 +16,14 @@ */ package org.apache.dubbo.metadata.rest; + import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import static org.apache.dubbo.common.utils.AnnotationUtils.getValue; -import static org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor.buildDefaultValue; +import static org.apache.dubbo.common.utils.ClassUtils.getClassLoader; +import static org.apache.dubbo.common.utils.ClassUtils.resolveClass; /** * The abstract {@link AnnotatedMethodParameterProcessor} implementation @@ -33,19 +35,39 @@ public abstract class AbstractAnnotatedMethodParameterProcessor implements Annot @Override public void process(Annotation annotation, Parameter parameter, int parameterIndex, Method method, Class serviceType, Class serviceInterfaceClass, RestMethodMetadata restMethodMetadata) { + String annotationValue = getAnnotationValue(annotation, parameter, parameterIndex); String defaultValue = getDefaultValue(annotation, parameter, parameterIndex); + addArgInfo(parameter, parameterIndex, restMethodMetadata, annotationValue, defaultValue); process(annotationValue, defaultValue, annotation, parameter, parameterIndex, method, restMethodMetadata); } + + protected void process(String annotationValue, String defaultValue, Annotation annotation, Parameter parameter, + int parameterIndex, Method method, RestMethodMetadata restMethodMetadata) { + + } + + + @Override + public Class getAnnotationClass() { + return resolveClass(getAnnotationName(), getClassLoader()); + } + + protected void addArgInfo(Parameter parameter, int parameterIndex, + RestMethodMetadata restMethodMetadata, String annotationValue, Object defaultValue) { + ArgInfo argInfo = ArgInfo.build(parameterIndex, parameter) + .setParamAnnotationType(getAnnotationClass()) + .setAnnotationNameAttribute(annotationValue).setDefaultValue(defaultValue); + restMethodMetadata.addArgInfo(argInfo); + } + protected String getAnnotationValue(Annotation annotation, Parameter parameter, int parameterIndex) { return getValue(annotation); } protected String getDefaultValue(Annotation annotation, Parameter parameter, int parameterIndex) { - return buildDefaultValue(parameterIndex); + return AnnotatedMethodParameterProcessor.buildDefaultValue(parameterIndex); } - protected abstract void process(String annotationValue, String defaultValue, Annotation annotation, Object parameter, - int parameterIndex, Method method, RestMethodMetadata restMethodMetadata); } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractNoAnnotatedParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractNoAnnotatedParameterProcessor.java new file mode 100644 index 00000000000..10748375b9c --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractNoAnnotatedParameterProcessor.java @@ -0,0 +1,68 @@ +/* + * 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.metadata.rest; + +import org.apache.dubbo.metadata.rest.media.MediaType; + +import java.lang.reflect.Parameter; +import java.util.Set; + +import static org.apache.dubbo.common.utils.ClassUtils.getClassLoader; +import static org.apache.dubbo.common.utils.ClassUtils.resolveClass; + +public abstract class AbstractNoAnnotatedParameterProcessor implements NoAnnotatedParameterRequestTagProcessor { + + public void process(Parameter parameter, int parameterIndex, RestMethodMetadata restMethodMetadata) { + MediaType mediaType = consumerContentType(); + if (!contentTypeSupport(restMethodMetadata, mediaType, parameter.getType())) { + return; + } + boolean isFormBody = isFormContentType(restMethodMetadata); + addArgInfo(parameter, parameterIndex, restMethodMetadata, isFormBody); + } + + private boolean contentTypeSupport(RestMethodMetadata restMethodMetadata, MediaType mediaType, Class paramType) { + + // @RequestParam String,number param + if (mediaType.equals(MediaType.ALL_VALUE) && (String.class == paramType || Number.class.isAssignableFrom(paramType))) { + return true; + } + + Set consumes = restMethodMetadata.getRequest().getConsumes(); + for (String consume : consumes) { + if (consume.contains(mediaType.value)) { + return true; + } + } + + return false; + } + + protected boolean isFormContentType(RestMethodMetadata restMethodMetadata) { + + return false; + } + + + protected void addArgInfo(Parameter parameter, int parameterIndex, + RestMethodMetadata restMethodMetadata, boolean isFormBody) { + ArgInfo argInfo = ArgInfo.build(parameterIndex, parameter) + .setParamAnnotationType(resolveClass(defaultAnnotationClassName(restMethodMetadata), getClassLoader())) + .setAnnotationNameAttribute(parameter.getName()).setFormContentType(isFormBody); + restMethodMetadata.addArgInfo(argInfo); + } +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java index 70434278dad..574a61e2c3e 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java @@ -43,6 +43,8 @@ import static org.apache.dubbo.common.utils.AnnotationUtils.isAnyAnnotationPresent; import static org.apache.dubbo.common.utils.ClassUtils.forName; import static org.apache.dubbo.common.utils.ClassUtils.getAllInterfaces; +import static org.apache.dubbo.common.utils.MemberUtils.isPrivate; +import static org.apache.dubbo.common.utils.MemberUtils.isStatic; import static org.apache.dubbo.common.utils.MethodUtils.excludedDeclaredClass; import static org.apache.dubbo.common.utils.MethodUtils.getAllMethods; import static org.apache.dubbo.common.utils.MethodUtils.overrides; @@ -56,13 +58,27 @@ public abstract class AbstractServiceRestMetadataResolver implements ServiceRestMetadataResolver { private final Map> parameterProcessorsMap; + private final Set noAnnotatedParameterRequestTagProcessors; public AbstractServiceRestMetadataResolver(ApplicationModel applicationModel) { this.parameterProcessorsMap = loadAnnotatedMethodParameterProcessors(applicationModel); + this.noAnnotatedParameterRequestTagProcessors = loadNoAnnotatedMethodParameterProcessors(applicationModel); } @Override public final boolean supports(Class serviceType) { + return supports(serviceType, false); + } + + @Override + public final boolean supports(Class serviceType, boolean consumer) { + // for consumer + if (consumer) { + // it is possible serviceType is impl + return supports0(serviceType); + } + + // for provider return isImplementedInterface(serviceType) && isServiceAnnotationPresent(serviceType) && supports0(serviceType); } @@ -72,7 +88,7 @@ protected final boolean isImplementedInterface(Class serviceType) { protected final boolean isServiceAnnotationPresent(Class serviceType) { return isAnyAnnotationPresent(serviceType, DubboService.class, Service.class, - com.alibaba.dubbo.config.annotation.Service.class); + com.alibaba.dubbo.config.annotation.Service.class); } /** @@ -85,12 +101,18 @@ protected final boolean isServiceAnnotationPresent(Class serviceType) { @Override public final ServiceRestMetadata resolve(Class serviceType) { - ServiceRestMetadata serviceRestMetadata = new ServiceRestMetadata(); // Process ServiceRestMetadata processServiceRestMetadata(serviceRestMetadata, serviceType); + return resolve(serviceType, serviceRestMetadata); + } + + + @Override + public final ServiceRestMetadata resolve(Class serviceType, ServiceRestMetadata serviceRestMetadata) { + serviceRestMetadata.setCodeStyle(this.getClass()); // Process RestMethodMetadata processAllRestMethodMetadata(serviceRestMetadata, serviceType); @@ -128,10 +150,10 @@ protected void processAllRestMethodMetadata(ServiceRestMetadata serviceRestMetad // try the overrider method first Method serviceMethod = entry.getKey(); // If failed, it indicates the overrider method does not contain metadata , then try the declared method - if (!processRestMethodMetadata(serviceMethod, serviceType, serviceInterfaceClass, serviceRestMetadata.getMeta()::add)) { + if (!processRestMethodMetadata(serviceMethod, serviceType, serviceInterfaceClass, serviceRestMetadata::addRestMethodMetadata)) { Method declaredServiceMethod = entry.getValue(); processRestMethodMetadata(declaredServiceMethod, serviceType, serviceInterfaceClass, - serviceRestMetadata.getMeta()::add); + serviceRestMetadata::addRestMethodMetadata); } } } @@ -148,8 +170,16 @@ protected Map resolveServiceMethodsMap(Class serviceType, Cla Map serviceMethodsMap = new LinkedHashMap<>(); // exclude the public methods declared in java.lang.Object.class List declaredServiceMethods = new ArrayList<>(getAllMethods(serviceInterfaceClass, excludedDeclaredClass(Object.class))); + + // for interface , such as consumer interface + if (serviceType.isInterface()) { + putInterfaceMethodToMap(serviceMethodsMap, declaredServiceMethods); + return unmodifiableMap(serviceMethodsMap); + } + List serviceMethods = new ArrayList<>(getAllMethods(serviceType, excludedDeclaredClass(Object.class))); + // sort methods sort(declaredServiceMethods, MethodComparator.INSTANCE); sort(serviceMethods, MethodComparator.INSTANCE); @@ -158,7 +188,8 @@ protected Map resolveServiceMethodsMap(Class serviceType, Cla for (Method serviceMethod : serviceMethods) { if (overrides(serviceMethod, declaredServiceMethod)) { serviceMethodsMap.put(serviceMethod, declaredServiceMethod); - continue; + // once method match ,break for decrease loop times + break; } } } @@ -166,6 +197,17 @@ protected Map resolveServiceMethodsMap(Class serviceType, Cla return unmodifiableMap(serviceMethodsMap); } + private void putInterfaceMethodToMap(Map serviceMethodsMap, List declaredServiceMethods) { + declaredServiceMethods.stream().forEach(method -> { + + // filter static private default + if (isStatic(method) || isPrivate(method) || method.isDefault()) { + return; + } + serviceMethodsMap.put(method, method); + }); + } + /** * Resolve the class of Dubbo Service interface * @@ -212,13 +254,15 @@ protected boolean processRestMethodMetadata(Method serviceMethod, Class servi RestMethodMetadata metadata = new RestMethodMetadata(); + metadata.setCodeStyle(this.getClass()); + + // to consumer service map + metadata.setReflectMethod(serviceMethod); + MethodDefinition methodDefinition = resolveMethodDefinition(serviceMethod, serviceType, serviceInterfaceClass); // Set MethodDefinition metadata.setMethod(methodDefinition); - // process the annotated method parameters - processAnnotatedMethodParameters(serviceMethod, serviceType, serviceInterfaceClass, metadata); - // process produces Set produces = new LinkedHashSet<>(); processProduces(serviceMethod, serviceType, serviceInterfaceClass, produces); @@ -234,6 +278,10 @@ protected boolean processRestMethodMetadata(Method serviceMethod, Class servi request.setProduces(produces); request.setConsumes(consumes); + // process the annotated method parameters + processAnnotatedMethodParameters(serviceMethod, serviceType, serviceInterfaceClass, metadata); + + // Post-Process postResolveRestMethodMetadata(serviceMethod, serviceType, serviceInterfaceClass, metadata); @@ -252,7 +300,7 @@ protected boolean processRestMethodMetadata(Method serviceMethod, Class servi * @return If capable, return true */ protected abstract boolean isRestCapableMethod(Method serviceMethod, Class serviceType, Class - serviceInterfaceClass); + serviceInterfaceClass); /** * Resolve the request method @@ -263,7 +311,7 @@ protected abstract boolean isRestCapableMethod(Method serviceMethod, Class se * @return if can't be resolve, return null */ protected abstract String resolveRequestMethod(Method serviceMethod, Class serviceType, Class - serviceInterfaceClass); + serviceInterfaceClass); /** * Resolve the request path @@ -274,7 +322,7 @@ protected abstract String resolveRequestMethod(Method serviceMethod, Class se * @return if can't be resolve, return null */ protected abstract String resolveRequestPath(Method serviceMethod, Class serviceType, Class - serviceInterfaceClass); + serviceInterfaceClass); /** * Resolve the {@link MethodDefinition} @@ -307,37 +355,56 @@ private void processAnnotatedMethodParameter(Parameter parameter, int parameterI Class serviceType, Class serviceInterfaceClass, RestMethodMetadata metadata) { Annotation[] annotations = parameter.getAnnotations(); + + if (annotations == null || annotations.length == 0) { + + for (NoAnnotatedParameterRequestTagProcessor processor : noAnnotatedParameterRequestTagProcessors) { + processor.process(parameter, parameterIndex, metadata); + } + return; + } + for (Annotation annotation : annotations) { String annotationType = annotation.annotationType().getName(); parameterProcessorsMap.getOrDefault(annotationType, emptyList()) - .forEach(processor -> { - processor.process(annotation, parameter, parameterIndex, serviceMethod, serviceType, - serviceInterfaceClass, metadata); - }); + .forEach(processor -> { + processor.process(annotation, parameter, parameterIndex, serviceMethod, serviceType, + serviceInterfaceClass, metadata); + }); } } protected abstract void processProduces(Method serviceMethod, Class serviceType, Class - serviceInterfaceClass, + serviceInterfaceClass, Set produces); protected abstract void processConsumes(Method serviceMethod, Class serviceType, Class - serviceInterfaceClass, + serviceInterfaceClass, Set consumes); protected void postResolveRestMethodMetadata(Method serviceMethod, Class serviceType, Class serviceInterfaceClass, RestMethodMetadata metadata) { + + // parse pathVariable index from url by annotation info + PathUtil.setArgInfoSplitIndex(metadata.getRequest().getPath(), metadata.getArgInfos()); } private static Map> loadAnnotatedMethodParameterProcessors(ApplicationModel applicationModel) { Map> parameterProcessorsMap = new LinkedHashMap<>(); applicationModel.getExtensionLoader(AnnotatedMethodParameterProcessor.class) - .getSupportedExtensionInstances() - .forEach(processor -> { - List processors = - parameterProcessorsMap.computeIfAbsent(processor.getAnnotationType(), k -> new LinkedList<>()); - processors.add(processor); - }); + .getSupportedExtensionInstances() + .forEach(processor -> { + List processors = + parameterProcessorsMap.computeIfAbsent(processor.getAnnotationName(), k -> new LinkedList<>()); + processors.add(processor); + }); return parameterProcessorsMap; } + + private static Set loadNoAnnotatedMethodParameterProcessors(ApplicationModel applicationModel) { + Set supportedExtensionInstances = applicationModel.getExtensionLoader(NoAnnotatedParameterRequestTagProcessor.class) + .getSupportedExtensionInstances(); + + return supportedExtensionInstances; + } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AnnotatedMethodParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AnnotatedMethodParameterProcessor.java index bd520179ad3..506b6cf6651 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AnnotatedMethodParameterProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AnnotatedMethodParameterProcessor.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.metadata.rest; +import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.lang.Prioritized; @@ -29,20 +30,28 @@ * * @since 2.7.6 */ -@SPI +@SPI(scope = ExtensionScope.FRAMEWORK) public interface AnnotatedMethodParameterProcessor extends Prioritized { + /** + * The string presenting the annotation name + * + * @return non-null + */ + String getAnnotationName(); + + /** * The string presenting the annotation type * * @return non-null */ - String getAnnotationType(); + Class getAnnotationClass(); /** * Process the specified method {@link VariableElement parameter} * - * @param annotation {@link Annotation the target annotation} whose type is {@link #getAnnotationType()} + * @param annotation {@link Annotation the target annotation} whose type is {@link #getAnnotationName()} * @param parameter the method parameter * @param parameterIndex the index of method parameter * @param method {@link Method method that parameter belongs to} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ArgInfo.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ArgInfo.java new file mode 100644 index 00000000000..599726e1e01 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ArgInfo.java @@ -0,0 +1,151 @@ +/* + * 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.metadata.rest; + + +import java.lang.reflect.Parameter; + +public class ArgInfo { + /** + * method arg index 0,1,2,3 + */ + private int index; + /** + * method annotation name or name + */ + private String annotationNameAttribute; + + /** + * param annotation type + */ + private Class paramAnnotationType; + + /** + * param Type + */ + private Class paramType; + + /** + * param name + */ + private String paramName; + + /** + * url split("/") String[n] index + */ + private int urlSplitIndex; + + private Object defaultValue; + + private boolean formContentType; + + public ArgInfo(int index, String name, Class paramType) { + this.index = index; + this.paramName = name; + this.paramType = paramType; + } + + public ArgInfo(int index, Parameter parameter) { + this(index, parameter.getName(), parameter.getType()); + } + + public ArgInfo() { + } + + public int getIndex() { + return index; + } + + public ArgInfo setIndex(int index) { + this.index = index; + return this; + } + + public String getAnnotationNameAttribute() { + if (annotationNameAttribute == null) { + // such as String param no annotation + return paramName; + } + return annotationNameAttribute; + } + + public ArgInfo setAnnotationNameAttribute(String annotationNameAttribute) { + this.annotationNameAttribute = annotationNameAttribute; + return this; + } + + public Class getParamAnnotationType() { + return paramAnnotationType; + } + + public ArgInfo setParamAnnotationType(Class paramAnnotationType) { + this.paramAnnotationType = paramAnnotationType; + return this; + } + + public Class getParamType() { + return paramType; + } + + public void setParamType(Class paramType) { + this.paramType = paramType; + } + + + public int getUrlSplitIndex() { + return urlSplitIndex; + } + + public void setUrlSplitIndex(int urlSplitIndex) { + this.urlSplitIndex = urlSplitIndex; + } + + public static ArgInfo build() { + return new ArgInfo(); + } + + public static ArgInfo build(int index, Parameter parameter) { + return new ArgInfo(index, parameter); + } + + public String getParamName() { + return paramName; + } + + public ArgInfo setParamName(String paramName) { + this.paramName = paramName; + return this; + } + + public Object getDefaultValue() { + return defaultValue; + } + + public ArgInfo setDefaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + return this; + } + + public boolean isFormContentType() { + return formContentType; + } + + public ArgInfo setFormContentType(boolean isFormContentType) { + this.formContentType = isFormContentType; + return this; + } +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/JAXRSClassConstants.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/JAXRSClassConstants.java new file mode 100644 index 00000000000..f1797cfc419 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/JAXRSClassConstants.java @@ -0,0 +1,62 @@ +/* + * 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.metadata.rest; + +import static org.apache.dubbo.common.utils.ClassUtils.getClassLoader; +import static org.apache.dubbo.common.utils.ClassUtils.resolveClass; + +public interface JAXRSClassConstants extends RestMetadataConstants.JAX_RS { + /** + * The annotation class of @Path + */ + Class PATH_ANNOTATION_CLASS = resolveClass(PATH_ANNOTATION_CLASS_NAME, getClassLoader()); + + + /** + * The annotation class of @FormParam + */ + Class FORM_PARAM_ANNOTATION_CLASS = resolveClass(FORM_PARAM_ANNOTATION_CLASS_NAME, getClassLoader()); + + /** + * The annotation class of @HeaderParam + */ + Class HEADER_PARAM_ANNOTATION_CLASS = resolveClass(HEADER_PARAM_ANNOTATION_CLASS_NAME, getClassLoader()); + + + /** + * The annotation class of @MatrixParam + */ + Class MATRIX_PARAM_ANNOTATION_CLASS = resolveClass(MATRIX_PARAM_ANNOTATION_CLASS_NAME, getClassLoader()); + + + /** + * The annotation class of @QueryParam + */ + Class QUERY_PARAM_ANNOTATION_CLASS = resolveClass(QUERY_PARAM_ANNOTATION_CLASS_NAME, getClassLoader()); + + /** + * The annotation class of @Body + */ + Class REST_EASY_BODY_ANNOTATION_CLASS = resolveClass(REST_EASY_BODY_ANNOTATION_CLASS_NAME, getClassLoader()); + + /** + * The annotation class of @PathParam + */ + Class PATH_PARAM_ANNOTATION_CLASS = resolveClass(PATH_PARAM_ANNOTATION_CLASS_NAME, getClassLoader()); + + +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/NoAnnotatedParameterRequestTagProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/NoAnnotatedParameterRequestTagProcessor.java new file mode 100644 index 00000000000..70f673c7e70 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/NoAnnotatedParameterRequestTagProcessor.java @@ -0,0 +1,32 @@ +/* + * 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.metadata.rest; + +import org.apache.dubbo.common.extension.ExtensionScope; +import org.apache.dubbo.common.extension.SPI; +import org.apache.dubbo.metadata.rest.media.MediaType; + +import java.lang.reflect.Parameter; + +@SPI(scope = ExtensionScope.FRAMEWORK) +public interface NoAnnotatedParameterRequestTagProcessor { + MediaType consumerContentType(); + + String defaultAnnotationClassName(RestMethodMetadata restMethodMetadata); + + void process(Parameter parameter, int parameterIndex, RestMethodMetadata restMethodMetadata); +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ParamType.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ParamType.java new file mode 100644 index 00000000000..f517794d639 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ParamType.java @@ -0,0 +1,97 @@ +/* + * 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.metadata.rest; + +import org.apache.dubbo.metadata.MetadataConstants; +import org.apache.dubbo.metadata.rest.tag.BodyTag; + +import java.util.ArrayList; +import java.util.List; + +public enum ParamType { + HEADER(addSupportTypes(JAXRSClassConstants.HEADER_PARAM_ANNOTATION_CLASS, + SpringMvcClassConstants.REQUEST_HEADER_ANNOTATION_CLASS)), + + PARAM(addSupportTypes(JAXRSClassConstants.QUERY_PARAM_ANNOTATION_CLASS, + SpringMvcClassConstants.REQUEST_PARAM_ANNOTATION_CLASS)), + + BODY(addSupportTypes( + JAXRSClassConstants.REST_EASY_BODY_ANNOTATION_CLASS, + SpringMvcClassConstants.REQUEST_BODY_ANNOTATION_CLASS, BodyTag.class)), + // TODO how to match arg type ? + REQ_OR_RES(addSupportTypes(MetadataConstants.JAKARTA_SERVLET_REQ_CLASS, + MetadataConstants.JAKARTA_SERVLET_RES_CLASS, + MetadataConstants.JAVAX_SERVLET_REQ_CLASS, + MetadataConstants.JAKARTA_SERVLET_RES_CLASS)), + + PATH(addSupportTypes(JAXRSClassConstants.PATH_PARAM_ANNOTATION_CLASS, + SpringMvcClassConstants.PATH_VARIABLE_ANNOTATION_CLASS)), + + FORM(addSupportTypes(JAXRSClassConstants.FORM_PARAM_ANNOTATION_CLASS)), + + EMPTY(addSupportTypes()); + private List annotationClasses; + + + ParamType(List annotationClasses) { + this.annotationClasses = annotationClasses; + } + + + public boolean supportAnno(Class anno) { + if (anno == null) { + return false; + } + return this.annotationClasses.contains(anno); + } + + public boolean isReqOrRes(Class clazz) { + for (Class annotationClass : annotationClasses) { + if (annotationClass.isAssignableFrom(clazz)) { + return true; + } + } + + return false; + } + + /** + * exclude null types + * + * @param classes + * @return + */ + private static List addSupportTypes(Class... classes) { + + ArrayList types = new ArrayList<>(); + + for (Class clazz : classes) { + + if (clazz == null) { + continue; + } + + types.add(clazz); + } + + return types; + + + } + + +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathMatcher.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathMatcher.java new file mode 100644 index 00000000000..6e7dcdcbe90 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathMatcher.java @@ -0,0 +1,146 @@ +/* + * 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.metadata.rest; + + +import java.util.Objects; + +public class PathMatcher { + private static final String SEPARATOR = "/"; + private String path; + private String version; + private String group; + private Integer port; + private String[] pathSplits; + private boolean hasPathVariable; + + + public PathMatcher(String path) { + this(path, null, null, 0); + } + + public PathMatcher(String path, String version, String group, Integer port) { + this.path = path; + this.pathSplits = path.split(SEPARATOR); + + for (String pathSplit : pathSplits) { + + if (isPlaceHold(pathSplit)) { + hasPathVariable = true; + break; + } + } + this.version = version; + this.group = group; + this.port = port; + } + + public void setPath(String path) { + this.path = path; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setGroup(String group) { + this.group = group; + } + + public void setPort(Integer port) { + this.port = port; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PathMatcher that = (PathMatcher) o; + return pathEqual(that.path) && Objects.equals(version, that.version) + && Objects.equals(group, that.group) && Objects.equals(port, that.port); + } + + @Override + public int hashCode() { + return Objects.hash(version, group, port); + } + + private boolean pathEqual(String path) { + + // no place hold + if (!hasPathVariable) { + return this.path.equals(path); + } + + String[] split = path.split(SEPARATOR); + + + if (split.length != pathSplits.length) { + return false; + } + + for (int i = 0; i < pathSplits.length; i++) { + boolean equals = split[i].equals(pathSplits[i]); + if (equals) { + continue; + } else { + if (placeHoldCompare(pathSplits[i])) { + continue; + } else { + return false; + } + } + } + + return true; + + } + + private boolean placeHoldCompare(String pathSplit) { + boolean startAndEndEqual = isPlaceHold(pathSplit); + + // start { end } + if (!startAndEndEqual) { + return false; + } + + // exclude {} + boolean lengthCondition = pathSplit.length() >= 3; + + if (!lengthCondition) { + return false; + } + + return true; + } + + private boolean isPlaceHold(String pathSplit) { + return pathSplit.startsWith("{") && pathSplit.endsWith("}"); + } + + + @Override + public String toString() { + return "PathMather{" + + "path='" + path + '\'' + + ", version='" + version + '\'' + + ", group='" + group + '\'' + + ", port='" + port + '\'' + + '}'; + } +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathUtil.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathUtil.java new file mode 100644 index 00000000000..f105461268b --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/PathUtil.java @@ -0,0 +1,157 @@ +/* + * 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.metadata.rest; + + +import org.apache.dubbo.metadata.MetadataConstants; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * is used to parse url pathVariable + *

+ * String[] splits= url.split("/") + * List strings = Arrays.asList(split); + * strings.set(UrlSplitIndex, (String) args.get(argIndex)); + */ +public class PathUtil { + private static final String SEPARATOR = MetadataConstants.PATH_SEPARATOR; + + /** + * generate real path from rawPath according to argInfo and method args + * + * @param rawPath + * @param argInfos + * @param args + * @return + */ + public static String resolvePathVariable(String rawPath, List argInfos, List args) { + + String[] split = rawPath.split(SEPARATOR); + + List strings = Arrays.asList(split); + + List pathArgInfos = new ArrayList<>(); + + for (ArgInfo argInfo : argInfos) { + if (ParamType.PATH.supportAnno(argInfo.getParamAnnotationType())) { + pathArgInfos.add(argInfo); + } + } + + + for (ArgInfo pathArgInfo : pathArgInfos) { + strings.set(pathArgInfo.getUrlSplitIndex(), String.valueOf(args.get(pathArgInfo.getIndex()))); + } + + + String pat = SEPARATOR; + + for (String string : strings) { + + if (string.length() == 0) { + continue; + } + + pat = pat + string + SEPARATOR; + } + + if (pat.endsWith(SEPARATOR)) { + pat = pat.substring(0, pat.lastIndexOf(SEPARATOR)); + } + + return pat; + + } + + + /** + * parse pathVariable index from url by annotation info + * + * @param rawPath + * @param argInfos + */ + public static void setArgInfoSplitIndex(String rawPath, List argInfos) { + String[] split = rawPath.split(SEPARATOR); + + List pathPairs = new ArrayList<>(); + + for (ArgInfo argInfo : argInfos) { + if (ParamType.PATH.supportAnno(argInfo.getParamAnnotationType())) { + pathPairs.add(new PathPair(argInfo)); + } + } + + for (int i = 0; i < split.length; i++) { + String s = split[i]; + for (PathPair pathPair : pathPairs) { + boolean match = pathPair.match(s); + if (match) { + pathPair.setArgInfoSplitIndex(i); + } + } + } + + } + + public static class PathPair { + + String value; + + ArgInfo argInfo; + + + public PathPair(ArgInfo argInfo) { + this.argInfo = argInfo; + this.value = argInfo.getAnnotationNameAttribute(); + } + + public String getPatten() { + return "{" + value + "}"; + } + + public String getLeftPatten() { + return "{" + value; + } + + public String getRightPatten() { + return "}"; + } + + public boolean match(String value) { + return getPatten().equals(value)// for : {id} + || (value.startsWith(getLeftPatten()) && value.endsWith(getRightPatten()));// for : {id: \d+} + } + + + public String getValue() { + return value; + } + + public void setArgInfo(ArgInfo argInfo) { + this.argInfo = argInfo; + } + + public void setArgInfoSplitIndex(int urlSplitIndex) { + this.argInfo.setUrlSplitIndex(urlSplitIndex); + } + } +} + + diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RequestMetadata.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RequestMetadata.java index 9a9983e4e88..77d2d4d2bb8 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RequestMetadata.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RequestMetadata.java @@ -55,6 +55,7 @@ public class RequestMetadata implements Serializable { private Set produces = new LinkedHashSet<>(); + /** * Default Constructor */ @@ -202,12 +203,12 @@ public boolean equals(Object o) { } RequestMetadata that = (RequestMetadata) o; return Objects.equals(method, that.method) - && Objects.equals(path, that.path) - && Objects.equals(consumes, that.consumes) - && Objects.equals(produces, that.produces) && - // Metadata should not compare the values - Objects.equals(getParamNames(), that.getParamNames()) - && Objects.equals(getHeaderNames(), that.getHeaderNames()); + && Objects.equals(path, that.path) + && Objects.equals(consumes, that.consumes) + && Objects.equals(produces, that.produces) && + // Metadata should not compare the values + Objects.equals(getParamNames(), that.getParamNames()) + && Objects.equals(getHeaderNames(), that.getHeaderNames()); } @@ -215,13 +216,13 @@ public boolean equals(Object o) { public int hashCode() { // The values of metadata should not use for the hashCode() method return Objects.hash(method, path, consumes, produces, getParamNames(), - getHeaderNames()); + getHeaderNames()); } @Override public String toString() { return "RequestMetadata{" + "method='" + method + '\'' + ", path='" + path + '\'' - + ", params=" + params + ", headers=" + headers + ", consumes=" + consumes - + ", produces=" + produces + '}'; + + ", params=" + params + ", headers=" + headers + ", consumes=" + consumes + + ", produces=" + produces + '}'; } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java index 6c758bf23b9..b96234d764b 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java @@ -87,6 +87,16 @@ interface JAX_RS { * The annotation class name of @QueryParam */ String QUERY_PARAM_ANNOTATION_CLASS_NAME = "javax.ws.rs.QueryParam"; + + /** + * The annotation class name of @Body + */ + String REST_EASY_BODY_ANNOTATION_CLASS_NAME = "org.jboss.resteasy.annotations.Body"; + + /** + * The annotation class name of @PathParam + */ + String PATH_PARAM_ANNOTATION_CLASS_NAME = "javax.ws.rs.PathParam"; } /** @@ -114,6 +124,16 @@ interface SPRING_MVC { */ String REQUEST_PARAM_ANNOTATION_CLASS_NAME = "org.springframework.web.bind.annotation.RequestParam"; + /** + * The annotation class name of @RequestBody + */ + String REQUEST_BODY_ANNOTATION_CLASS_NAME = "org.springframework.web.bind.annotation.RequestBody"; + + /** + * The annotation class name of @PathVariable + */ + String PATH_VARIABLE_ANNOTATION_CLASS_NAME = "org.springframework.web.bind.annotation.PathVariable"; + /** * The class of @Controller * diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java index 1531c973fda..a2c718c563f 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java @@ -19,9 +19,10 @@ import org.apache.dubbo.metadata.definition.model.MethodDefinition; import java.io.Serializable; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -56,6 +57,17 @@ public class RestMethodMetadata implements Serializable { private Map indexToEncoded; + private ServiceRestMetadata serviceRestMetadata; + + private List argInfos; + + private Method reflectMethod; + + /** + * make a distinction between mvc & resteasy + */ + private Class codeStyle; + public MethodDefinition getMethod() { if (method == null) { method = new MethodDefinition(); @@ -112,7 +124,7 @@ public void setBodyType(String bodyType) { public Map> getIndexToName() { if (indexToName == null) { - indexToName = new HashMap<>(); + indexToName = new LinkedHashMap<>(); } return indexToName; } @@ -157,6 +169,43 @@ public void setIndexToEncoded(Map indexToEncoded) { this.indexToEncoded = indexToEncoded; } + + public ServiceRestMetadata getServiceRestMetadata() { + return serviceRestMetadata; + } + + public void setServiceRestMetadata(ServiceRestMetadata serviceRestMetadata) { + this.serviceRestMetadata = serviceRestMetadata; + } + + public List getArgInfos() { + if (argInfos == null) { + argInfos = new ArrayList<>(); + } + return argInfos; + } + + public void addArgInfo(ArgInfo argInfo) { + getArgInfos().add(argInfo); + } + + + public Method getReflectMethod() { + return reflectMethod; + } + + public void setReflectMethod(Method reflectMethod) { + this.reflectMethod = reflectMethod; + } + + public Class getCodeStyle() { + return codeStyle; + } + + public void setCodeStyle(Class codeStyle) { + this.codeStyle = codeStyle; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -167,13 +216,13 @@ public boolean equals(Object o) { } RestMethodMetadata that = (RestMethodMetadata) o; return Objects.equals(getMethod(), that.getMethod()) && - Objects.equals(getRequest(), that.getRequest()) && - Objects.equals(getUrlIndex(), that.getUrlIndex()) && - Objects.equals(getBodyIndex(), that.getBodyIndex()) && - Objects.equals(getHeaderMapIndex(), that.getHeaderMapIndex()) && - Objects.equals(getBodyType(), that.getBodyType()) && - Objects.equals(getFormParams(), that.getFormParams()) && - Objects.equals(getIndexToEncoded(), that.getIndexToEncoded()); + Objects.equals(getRequest(), that.getRequest()) && + Objects.equals(getUrlIndex(), that.getUrlIndex()) && + Objects.equals(getBodyIndex(), that.getBodyIndex()) && + Objects.equals(getHeaderMapIndex(), that.getHeaderMapIndex()) && + Objects.equals(getBodyType(), that.getBodyType()) && + Objects.equals(getFormParams(), that.getFormParams()) && + Objects.equals(getIndexToEncoded(), that.getIndexToEncoded()); } @Override diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java index df396b0d703..19f0aab77ba 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java @@ -16,8 +16,12 @@ */ package org.apache.dubbo.metadata.rest; +import org.apache.dubbo.metadata.ParameterTypesComparator; + import java.io.Serializable; +import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -39,6 +43,32 @@ public class ServiceRestMetadata implements Serializable { private Set meta; + private Integer port; + + private boolean consumer; + + /** + * make a distinction between mvc & resteasy + */ + private Class codeStyle; + + private Map pathToServiceMap = new HashMap<>(); + private Map> methodToServiceMap = new HashMap<>(); + + public ServiceRestMetadata(String serviceInterface, String version, String group, boolean consumer) { + this.serviceInterface = serviceInterface; + this.version = version; + this.group = group; + this.consumer = consumer; + } + + public ServiceRestMetadata() { + } + + public ServiceRestMetadata(String serviceInterface, String version, String group) { + this(serviceInterface, version, group, false); + } + public String getServiceInterface() { return serviceInterface; } @@ -74,6 +104,70 @@ public void setMeta(Set meta) { this.meta = meta; } + public void addRestMethodMetadata(RestMethodMetadata restMethodMetadata) { + restMethodMetadata.setServiceRestMetadata(this); + PathMatcher pathMather = new PathMatcher(restMethodMetadata.getRequest().getPath(), + this.getVersion(), this.getGroup(), this.getPort()); + addPathToServiceMap(pathMather, restMethodMetadata); + addMethodToServiceMap(restMethodMetadata); + getMeta().add(restMethodMetadata); + } + + public Map getPathToServiceMap() { + return pathToServiceMap; + } + + public void addPathToServiceMap(PathMatcher pathMather, RestMethodMetadata restMethodMetadata) { + if (this.pathToServiceMap == null) { + this.pathToServiceMap = new HashMap<>(); + } + + this.pathToServiceMap.put(pathMather, restMethodMetadata); + + + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + Map pathToServiceMap = getPathToServiceMap(); + for (PathMatcher pathMather : pathToServiceMap.keySet()) { + pathMather.setPort(port); + } + } + + public boolean isConsumer() { + return consumer; + } + + public void setConsumer(boolean consumer) { + this.consumer = consumer; + } + + public Map> getMethodToServiceMap() { + return methodToServiceMap; + } + + public void addMethodToServiceMap(RestMethodMetadata restMethodMetadata) { + if (this.methodToServiceMap == null) { + this.methodToServiceMap = new HashMap<>(); + } + + this.methodToServiceMap.computeIfAbsent(restMethodMetadata.getReflectMethod().getName(), k -> new HashMap<>()) + .put(ParameterTypesComparator.getInstance(restMethodMetadata.getReflectMethod().getParameterTypes()), restMethodMetadata); + } + + public Class getCodeStyle() { + return codeStyle; + } + + public void setCodeStyle(Class codeStyle) { + this.codeStyle = codeStyle; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -84,14 +178,15 @@ public boolean equals(Object o) { } ServiceRestMetadata that = (ServiceRestMetadata) o; return Objects.equals(getServiceInterface(), that.getServiceInterface()) && - Objects.equals(getVersion(), that.getVersion()) && - Objects.equals(getGroup(), that.getGroup()) && - Objects.equals(getMeta(), that.getMeta()); + Objects.equals(getVersion(), that.getVersion()) && + Objects.equals(getGroup(), that.getGroup()) && + Objects.equals(getMeta(), that.getMeta()) && + Objects.equals(getPort(), that.getPort()); } @Override public int hashCode() { - return Objects.hash(getServiceInterface(), getVersion(), getGroup(), getMeta()); + return Objects.hash(getServiceInterface(), getVersion(), getGroup(), getMeta(), getPort()); } @Override @@ -101,6 +196,7 @@ public String toString() { sb.append(", version='").append(version).append('\''); sb.append(", group='").append(group).append('\''); sb.append(", meta=").append(meta); + sb.append(", port=").append(port); sb.append('}'); return sb.toString(); } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadataResolver.java index 41ea25d9995..492dc942fbf 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadataResolver.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadataResolver.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.metadata.rest; +import org.apache.dubbo.common.extension.ExtensionScope; import org.apache.dubbo.common.extension.SPI; /** @@ -24,7 +25,7 @@ * * @since 2.7.6 */ -@SPI +@SPI(scope = ExtensionScope.APPLICATION) public interface ServiceRestMetadataResolver { /** @@ -35,6 +36,8 @@ public interface ServiceRestMetadataResolver { */ boolean supports(Class serviceType); + boolean supports(Class serviceType,boolean consumer); + /** * Resolve the {@link ServiceRestMetadata REST metadata} from the specified * Dubbo Service interface or type @@ -43,4 +46,6 @@ public interface ServiceRestMetadataResolver { * @return */ ServiceRestMetadata resolve(Class serviceType); + + ServiceRestMetadata resolve(Class serviceType, ServiceRestMetadata serviceRestMetadata); } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/SpringMvcClassConstants.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/SpringMvcClassConstants.java new file mode 100644 index 00000000000..bd3cabf4db2 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/SpringMvcClassConstants.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata.rest; + +import static org.apache.dubbo.common.utils.ClassUtils.getClassLoader; +import static org.apache.dubbo.common.utils.ClassUtils.resolveClass; + +public interface SpringMvcClassConstants extends RestMetadataConstants.SPRING_MVC { + /** + * The annotation class of @RequestMapping + */ + Class REQUEST_MAPPING_ANNOTATION_CLASS = resolveClass(REQUEST_MAPPING_ANNOTATION_CLASS_NAME, getClassLoader()); + + + /** + * The annotation class of @RequestHeader + */ + Class REQUEST_HEADER_ANNOTATION_CLASS = resolveClass(REQUEST_HEADER_ANNOTATION_CLASS_NAME, getClassLoader()); + + + /** + * The annotation class of @RequestParam + */ + Class REQUEST_PARAM_ANNOTATION_CLASS = resolveClass(REQUEST_PARAM_ANNOTATION_CLASS_NAME, getClassLoader()); + + + /** + * The annotation class of @RequestBody + */ + Class REQUEST_BODY_ANNOTATION_CLASS = resolveClass(REQUEST_BODY_ANNOTATION_CLASS_NAME, getClassLoader()); + + + /** + * The annotation class of @RequestBody + */ + Class PATH_VARIABLE_ANNOTATION_CLASS= resolveClass(PATH_VARIABLE_ANNOTATION_CLASS_NAME, getClassLoader()); +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/BodyParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/BodyParameterProcessor.java new file mode 100644 index 00000000000..98895534707 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/BodyParameterProcessor.java @@ -0,0 +1,52 @@ +/* + * 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.metadata.rest.jaxrs; + +import org.apache.dubbo.metadata.rest.AbstractAnnotatedMethodParameterProcessor; +import org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor; +import org.apache.dubbo.metadata.rest.ArgInfo; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; + +import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.REST_EASY_BODY_ANNOTATION_CLASS_NAME; + +/** + * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @FormParam + * + * @since 2.7.6 + */ +public class BodyParameterProcessor extends AbstractAnnotatedMethodParameterProcessor { + + @Override + public String getAnnotationName() { + return REST_EASY_BODY_ANNOTATION_CLASS_NAME; + } + + @Override + public void process(Annotation annotation, Parameter parameter, int parameterIndex, Method method, Class serviceType, Class serviceInterfaceClass, RestMethodMetadata restMethodMetadata) { + ArgInfo argInfo = ArgInfo. + build(parameterIndex, parameter). + setParamAnnotationType(getAnnotationClass()); + restMethodMetadata.addArgInfo(argInfo); + + } + + +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/DefaultValueParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/DefaultValueParameterProcessor.java index a3bfe8d4bae..475fbe9cd1f 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/DefaultValueParameterProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/DefaultValueParameterProcessor.java @@ -23,6 +23,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import java.util.List; import java.util.Map; @@ -38,12 +39,12 @@ public class DefaultValueParameterProcessor extends AbstractAnnotatedMethodParameterProcessor { @Override - public String getAnnotationType() { + public String getAnnotationName() { return DEFAULT_VALUE_ANNOTATION_CLASS_NAME; } @Override - protected void process(String annotationValue, String defaultValue, Annotation annotation, Object parameter, + protected void process(String annotationValue, String defaultValue, Annotation annotation, Parameter parameter, int parameterIndex, Method method, RestMethodMetadata restMethodMetadata) { RequestMetadata requestMetadata = restMethodMetadata.getRequest(); diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/FormParamParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/FormParamParameterProcessor.java index d102e397b7c..6dae712118f 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/FormParamParameterProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/FormParamParameterProcessor.java @@ -28,7 +28,7 @@ public class FormParamParameterProcessor extends ParamAnnotationParameterProcessor { @Override - public String getAnnotationType() { + public String getAnnotationName() { return FORM_PARAM_ANNOTATION_CLASS_NAME; } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/HeaderParamParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/HeaderParamParameterProcessor.java index 16d21bfd3bd..216f1091f20 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/HeaderParamParameterProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/HeaderParamParameterProcessor.java @@ -23,6 +23,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import static org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor.buildDefaultValue; import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.HEADER_PARAM_ANNOTATION_CLASS_NAME; @@ -35,12 +36,12 @@ public class HeaderParamParameterProcessor extends AbstractAnnotatedMethodParameterProcessor { @Override - public String getAnnotationType() { + public String getAnnotationName() { return HEADER_PARAM_ANNOTATION_CLASS_NAME; } @Override - protected void process(String headerName, String defaultValue, Annotation annotation, Object parameter, + protected void process(String headerName, String defaultValue, Annotation annotation, Parameter parameter, int parameterIndex, Method method, RestMethodMetadata restMethodMetadata) { RequestMetadata requestMetadata = restMethodMetadata.getRequest(); // Add the placeholder as header value diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolver.java index 35df2e7a7af..1e107edc22b 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolver.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolver.java @@ -85,12 +85,16 @@ private String resolveRequestPathFromMethod(Method serviceMethod) { protected void processProduces(Method serviceMethod, Class serviceType, Class serviceInterfaceClass, Set produces) { addAnnotationValues(serviceMethod, PRODUCES_ANNOTATION_CLASS_NAME, produces); + addAnnotationValues(serviceType, PRODUCES_ANNOTATION_CLASS_NAME, produces); + addAnnotationValues(serviceInterfaceClass, PRODUCES_ANNOTATION_CLASS_NAME, produces); } @Override protected void processConsumes(Method serviceMethod, Class serviceType, Class serviceInterfaceClass, Set consumes) { addAnnotationValues(serviceMethod, CONSUMES_ANNOTATION_CLASS_NAME, consumes); + addAnnotationValues(serviceType, CONSUMES_ANNOTATION_CLASS_NAME, consumes); + addAnnotationValues(serviceInterfaceClass, CONSUMES_ANNOTATION_CLASS_NAME, consumes); } private void addAnnotationValues(Method serviceMethod, String annotationAttributeName, Set result) { @@ -100,4 +104,12 @@ private void addAnnotationValues(Method serviceMethod, String annotationAttribut Stream.of(value).forEach(result::add); } } + + private void addAnnotationValues(Class serviceType, String annotationAttributeName, Set result) { + Annotation annotation = findAnnotation(serviceType, annotationAttributeName); + String[] value = getValue(annotation); + if (value != null) { + Stream.of(value).forEach(result::add); + } + } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/MatrixParamParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/MatrixParamParameterProcessor.java index 894f33db2aa..a75284a6898 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/MatrixParamParameterProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/MatrixParamParameterProcessor.java @@ -28,7 +28,7 @@ public class MatrixParamParameterProcessor extends ParamAnnotationParameterProcessor { @Override - public String getAnnotationType() { + public String getAnnotationName() { return MATRIX_PARAM_ANNOTATION_CLASS_NAME; } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/ParamAnnotationParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/ParamAnnotationParameterProcessor.java index b6be293e16b..d7dd5c05c98 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/ParamAnnotationParameterProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/ParamAnnotationParameterProcessor.java @@ -23,6 +23,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.lang.reflect.Parameter; /** * The abstract {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @*Param @@ -30,7 +31,7 @@ public abstract class ParamAnnotationParameterProcessor extends AbstractAnnotatedMethodParameterProcessor { @Override - protected void process(String name, String defaultValue, Annotation annotation, Object parameter, + protected void process(String name, String defaultValue, Annotation annotation, Parameter parameter, int parameterIndex, Method method, RestMethodMetadata restMethodMetadata) { RequestMetadata requestMetadata = restMethodMetadata.getRequest(); requestMetadata.addParam(name, defaultValue); diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/PathParamParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/PathParamParameterProcessor.java new file mode 100644 index 00000000000..3ab3b8591dc --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/PathParamParameterProcessor.java @@ -0,0 +1,37 @@ +/* + * 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.metadata.rest.jaxrs; + +import org.apache.dubbo.metadata.rest.AbstractAnnotatedMethodParameterProcessor; +import org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor; + +import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.PATH_PARAM_ANNOTATION_CLASS_NAME; + +/** + * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @PathParam + * + * @since 2.7.6 + */ +public class PathParamParameterProcessor extends AbstractAnnotatedMethodParameterProcessor { + + @Override + public String getAnnotationName() { + return PATH_PARAM_ANNOTATION_CLASS_NAME; + } + + +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/QueryParamParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/QueryParamParameterProcessor.java index bfe539c9cc5..ed6972f17ac 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/QueryParamParameterProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/QueryParamParameterProcessor.java @@ -28,7 +28,7 @@ public class QueryParamParameterProcessor extends ParamAnnotationParameterProcessor { @Override - public String getAnnotationType() { + public String getAnnotationName() { return QUERY_PARAM_ANNOTATION_CLASS_NAME; } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/media/MediaType.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/media/MediaType.java new file mode 100644 index 00000000000..8b5c96e4056 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/media/MediaType.java @@ -0,0 +1,46 @@ +/* + * 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.metadata.rest.media; + + +public enum MediaType { + ALL_VALUE("*/*"), + APPLICATION_JSON_VALUE("application/json"), + APPLICATION_FORM_URLENCODED_VALUE("application/x-www-form-urlencoded"), + TEXT_PLAIN("text/plain"), + TEXT_XML("text/xml"), + OCTET_STREAM("application/octet-stream"), + ; + + MediaType(String value) { + this.value = value; + } + + public String value; + + public static String getAllContentType() { + + MediaType[] values = MediaType.values(); + + StringBuilder stringBuilder = new StringBuilder(); + + for (MediaType mediaType : values) { + stringBuilder.append(mediaType.value + " "); + } + return stringBuilder.toString(); + } +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/FormBodyNoAnnotatedProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/FormBodyNoAnnotatedProcessor.java new file mode 100644 index 00000000000..60bcbc92659 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/FormBodyNoAnnotatedProcessor.java @@ -0,0 +1,41 @@ +/* + * 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.metadata.rest.springmvc; + +import org.apache.dubbo.metadata.rest.AbstractNoAnnotatedParameterProcessor; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.metadata.rest.media.MediaType; + +import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_BODY_ANNOTATION_CLASS_NAME; +import static org.apache.dubbo.metadata.rest.media.MediaType.APPLICATION_FORM_URLENCODED_VALUE; + +public class FormBodyNoAnnotatedProcessor extends AbstractNoAnnotatedParameterProcessor { + @Override + public MediaType consumerContentType() { + return APPLICATION_FORM_URLENCODED_VALUE; + } + + @Override + public String defaultAnnotationClassName(RestMethodMetadata restMethodMetadata) { + return REQUEST_BODY_ANNOTATION_CLASS_NAME; + } + + @Override + protected boolean isFormContentType(RestMethodMetadata restMethodMetadata) { + return true; + } +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/JsonBodyNoAnnotatedProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/JsonBodyNoAnnotatedProcessor.java new file mode 100644 index 00000000000..7fc55d55385 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/JsonBodyNoAnnotatedProcessor.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata.rest.springmvc; + +import org.apache.dubbo.metadata.rest.AbstractNoAnnotatedParameterProcessor; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.metadata.rest.media.MediaType; + +import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_BODY_ANNOTATION_CLASS_NAME; +import static org.apache.dubbo.metadata.rest.media.MediaType.APPLICATION_JSON_VALUE; + +public class JsonBodyNoAnnotatedProcessor extends AbstractNoAnnotatedParameterProcessor { + @Override + public MediaType consumerContentType() { + return APPLICATION_JSON_VALUE; + } + + @Override + public String defaultAnnotationClassName(RestMethodMetadata restMethodMetadata) { + return REQUEST_BODY_ANNOTATION_CLASS_NAME; + } +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/ParamNoAnnotatedProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/ParamNoAnnotatedProcessor.java new file mode 100644 index 00000000000..b0591e4886c --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/ParamNoAnnotatedProcessor.java @@ -0,0 +1,43 @@ +/* + * 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.metadata.rest.springmvc; + +import org.apache.dubbo.metadata.rest.AbstractNoAnnotatedParameterProcessor; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver; +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.metadata.rest.tag.BodyTag; + +import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_PARAM_ANNOTATION_CLASS_NAME; +import static org.apache.dubbo.metadata.rest.media.MediaType.ALL_VALUE; + +public class ParamNoAnnotatedProcessor extends AbstractNoAnnotatedParameterProcessor { + @Override + public MediaType consumerContentType() { + return ALL_VALUE; + } + + @Override + public String defaultAnnotationClassName(RestMethodMetadata restMethodMetadata) { + + if (JAXRSServiceRestMetadataResolver.class.equals(restMethodMetadata.getCodeStyle())) { + return BodyTag.class.getName(); + } + + return REQUEST_PARAM_ANNOTATION_CLASS_NAME; + } +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/PathVariableParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/PathVariableParameterProcessor.java new file mode 100644 index 00000000000..a1eccd888b9 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/PathVariableParameterProcessor.java @@ -0,0 +1,43 @@ +/* + * 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.metadata.rest.springmvc; + +import org.apache.dubbo.metadata.rest.AbstractAnnotatedMethodParameterProcessor; +import org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Parameter; + +import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.PATH_VARIABLE_ANNOTATION_CLASS_NAME; + +/** + * The {@link AnnotatedMethodParameterProcessor} implementation for Spring Web MVC's @PathVariable + */ +public class PathVariableParameterProcessor extends AbstractAnnotatedMethodParameterProcessor { + + @Override + public String getAnnotationName() { + return PATH_VARIABLE_ANNOTATION_CLASS_NAME; + } + + + @Override + protected String getDefaultValue(Annotation annotation, Parameter parameter, int parameterIndex) { + return null; + } +} + diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/RequestBodyParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/RequestBodyParameterProcessor.java new file mode 100644 index 00000000000..0c93615e68b --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/RequestBodyParameterProcessor.java @@ -0,0 +1,46 @@ +/* + * 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.metadata.rest.springmvc; + +import org.apache.dubbo.metadata.rest.AbstractAnnotatedMethodParameterProcessor; +import org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Parameter; + +import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_BODY_ANNOTATION_CLASS_NAME; + +/** + * The {@link AnnotatedMethodParameterProcessor} implementation for Spring Web MVC's @RequestBody + */ +public class RequestBodyParameterProcessor extends AbstractAnnotatedMethodParameterProcessor { + + @Override + public String getAnnotationName() { + return REQUEST_BODY_ANNOTATION_CLASS_NAME; + } + + @Override + protected String getAnnotationValue(Annotation annotation, Parameter parameter, int parameterIndex) { + return null; + } + + @Override + protected String getDefaultValue(Annotation annotation, Parameter parameter, int parameterIndex) { + return null; + } +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/RequestHeaderParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/RequestHeaderParameterProcessor.java index 8d168984620..3eaae48e4fc 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/RequestHeaderParameterProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/RequestHeaderParameterProcessor.java @@ -21,6 +21,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_HEADER_ANNOTATION_CLASS_NAME; @@ -30,12 +31,12 @@ public class RequestHeaderParameterProcessor extends AbstractRequestAnnotationParameterProcessor { @Override - public String getAnnotationType() { + public String getAnnotationName() { return REQUEST_HEADER_ANNOTATION_CLASS_NAME; } @Override - protected void process(String name, String defaultValue, Annotation annotation, Object parameter, + protected void process(String name, String defaultValue, Annotation annotation, Parameter parameter, int parameterIndex, Method method, RestMethodMetadata restMethodMetadata) { restMethodMetadata.getRequest().addHeader(name, defaultValue); } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/RequestParamParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/RequestParamParameterProcessor.java index 5ae4727f3be..7549196fe40 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/RequestParamParameterProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/RequestParamParameterProcessor.java @@ -21,6 +21,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_PARAM_ANNOTATION_CLASS_NAME; @@ -30,14 +31,13 @@ public class RequestParamParameterProcessor extends AbstractRequestAnnotationParameterProcessor { @Override - public String getAnnotationType() { + public String getAnnotationName() { return REQUEST_PARAM_ANNOTATION_CLASS_NAME; } @Override - protected void process(String name, String defaultValue, Annotation annotation, Object parameter, int parameterIndex, + protected void process(String name, String defaultValue, Annotation annotation, Parameter parameter, int parameterIndex, Method method, RestMethodMetadata restMethodMetadata) { restMethodMetadata.getRequest().addParam(name, defaultValue); - } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java index bd94509e7a5..135696df6a4 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java @@ -89,11 +89,15 @@ protected String resolveRequestPath(Method serviceMethod, Class serviceType, @Override protected void processProduces(Method serviceMethod, Class serviceType, Class serviceInterfaceClass, Set produces) { addMediaTypes(serviceMethod, "produces", produces); + addMediaTypes(serviceType, "produces", produces); + addMediaTypes(serviceInterfaceClass, "produces", produces); } @Override protected void processConsumes(Method serviceMethod, Class serviceType, Class serviceInterfaceClass, Set consumes) { addMediaTypes(serviceMethod, "consumes", consumes); + addMediaTypes(serviceType, "consumes", consumes); + addMediaTypes(serviceInterfaceClass, "consumes", consumes); } private String resolveRequestPath(AnnotatedElement annotatedElement) { @@ -124,6 +128,17 @@ private void addMediaTypes(Method serviceMethod, String annotationAttributeName, } } + private void addMediaTypes(Class serviceType, String annotationAttributeName, Set mediaTypesSet) { + + Annotation mappingAnnotation = getRequestMapping(serviceType); + + String[] mediaTypes = getAttribute(mappingAnnotation, annotationAttributeName); + + if (isNotEmpty(mediaTypes)) { + of(mediaTypes).forEach(mediaTypesSet::add); + } + } + private Annotation getRequestMapping(AnnotatedElement annotatedElement) { // try "@RequestMapping" first Annotation requestMapping = findAnnotation(annotatedElement, REQUEST_MAPPING_ANNOTATION_CLASS); diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/tag/BodyTag.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/tag/BodyTag.java new file mode 100644 index 00000000000..67dd291a8ee --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/tag/BodyTag.java @@ -0,0 +1,20 @@ +/* + * 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.metadata.rest.tag; + +public class BodyTag { +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor index bcb620cdbe8..c4df3bec14d 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor +++ b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor @@ -4,7 +4,11 @@ jax-rs.form-param = org.apache.dubbo.metadata.rest.jaxrs.FormParamParameterProce jax-rs.matrix-param = org.apache.dubbo.metadata.rest.jaxrs.MatrixParamParameterProcessor jax-rs.header-param = org.apache.dubbo.metadata.rest.jaxrs.HeaderParamParameterProcessor jax-rs.default-value-param = org.apache.dubbo.metadata.rest.jaxrs.DefaultValueParameterProcessor +jax-rs.body = org.apache.dubbo.metadata.rest.jaxrs.BodyParameterProcessor +jax-rs.path-param = org.apache.dubbo.metadata.rest.jaxrs.PathParamParameterProcessor # Spring Web MVC's implementations spring-webmvc.request-param = org.apache.dubbo.metadata.rest.springmvc.RequestParamParameterProcessor -spring-webmvc.request-header = org.apache.dubbo.metadata.rest.springmvc.RequestHeaderParameterProcessor \ No newline at end of file +spring-webmvc.request-header = org.apache.dubbo.metadata.rest.springmvc.RequestHeaderParameterProcessor +spring-webmvc.request-body = org.apache.dubbo.metadata.rest.springmvc.RequestBodyParameterProcessor +spring-webmvc.request-path = org.apache.dubbo.metadata.rest.springmvc.PathVariableParameterProcessor diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.NoAnnotatedParameterRequestTagProcessor b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.NoAnnotatedParameterRequestTagProcessor new file mode 100644 index 00000000000..5324f222215 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.NoAnnotatedParameterRequestTagProcessor @@ -0,0 +1,3 @@ +body-form=org.apache.dubbo.metadata.rest.springmvc.FormBodyNoAnnotatedProcessor +body-json=org.apache.dubbo.metadata.rest.springmvc.JsonBodyNoAnnotatedProcessor +param=org.apache.dubbo.metadata.rest.springmvc.ParamNoAnnotatedProcessor diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/PathMatcherTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/PathMatcherTest.java new file mode 100644 index 00000000000..e2498c2b956 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/PathMatcherTest.java @@ -0,0 +1,32 @@ +/* + * 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.metadata; + +import org.apache.dubbo.metadata.rest.PathMatcher; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class PathMatcherTest { + + @Test + public void testPathMatcher() { + PathMatcher pathMather = new PathMatcher("/a/b/c/{path1}/d/{path2}/e"); + + PathMatcher pathMather1 = new PathMatcher("/a/b/c/1/d/2/e"); + Assertions.assertEquals(true, pathMather.equals(pathMather1)); + } +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/DefaultRestService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/DefaultRestService.java index 210952a86ba..fd0f36cc62c 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/DefaultRestService.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/DefaultRestService.java @@ -63,6 +63,21 @@ public Map requestBodyUser(User user) { return null; } + @Override + public void noAnnotationJsonBody(User user) { + + } + + @Override + public void noAnnotationFormBody(User user) { + + } + + @Override + public void noAnnotationParam(String text) { + + } + public User user(User user) { return user; } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/RestService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/RestService.java index 65d67ab475d..60c78cf3ae4 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/RestService.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/RestService.java @@ -39,4 +39,8 @@ public interface RestService { User requestBodyMap(Map data, String param); Map requestBodyUser(User user); + + void noAnnotationJsonBody(User user); + void noAnnotationFormBody(User user); + void noAnnotationParam(String text); } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java index c2daf9b1045..de26899585e 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java @@ -19,13 +19,13 @@ import org.apache.dubbo.config.annotation.DubboService; import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; import java.util.HashMap; import java.util.Map; @@ -92,4 +92,22 @@ public Map requestBodyUser(@RequestBody User user) { map.put("age", user.getAge()); return map; } + + @PostMapping(value = "/request/body/user/json", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE) + @Override + public void noAnnotationJsonBody(User user) { + + } + + @PostMapping(value = "/request/body/user/form", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + @Override + public void noAnnotationFormBody(User user) { + + } + + @PostMapping(value = "/request/body/user/param") + @Override + public void noAnnotationParam(String text) { + + } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java index 684d76fd3b2..44ee3514c6d 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java @@ -27,6 +27,7 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; import java.util.HashMap; import java.util.Map; @@ -104,4 +105,27 @@ public Map requestBodyUser(User user) { map.put("age", user.getAge()); return map; } + + @Path("noAnnotationJsonBody/json") + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Override + public void noAnnotationJsonBody(User user) { + + } + + @Path("noAnnotationFormBody/form") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @POST + @Override + public void noAnnotationFormBody(User user) { + + } + + @Path("noAnnotationParam/text") + @POST + @Override + public void noAnnotationParam(String text) { + + } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/AnotherUserRestService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/AnotherUserRestService.java new file mode 100644 index 00000000000..7afb0fc37a8 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/AnotherUserRestService.java @@ -0,0 +1,45 @@ +/* + * 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.metadata.rest.api; + +import org.apache.dubbo.metadata.rest.User; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("u") +@Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) +@Produces({MediaType.APPLICATION_JSON}) +public interface AnotherUserRestService { + + @GET + @Path("{id : \\d+}") + User getUser(@PathParam("id") Long id); + + @POST + @Path("register") + String registerUser(User user); + + @GET + @Path("context") + String getContext(); +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestService.java new file mode 100644 index 00000000000..ec25773e713 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestService.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.metadata.rest.api; + +import org.apache.dubbo.metadata.rest.User; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; + +public interface JaxrsRestService { + + @Path("/param") + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.TEXT_PLAIN) + @GET + String param(@QueryParam("param") String param); + + @Path("/header") + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.TEXT_PLAIN) + @GET + String header(@HeaderParam("header") String header); + + @Path("/body") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @POST + User body(User user); + + @Path("/multiValue") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.APPLICATION_FORM_URLENCODED) + @POST + MultivaluedMap multiValue(MultivaluedMap map); + + @Path("/pathVariable/{a}") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.APPLICATION_FORM_URLENCODED) + @POST + String pathVariable(@PathParam("a") String a); + + +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestServiceImpl.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestServiceImpl.java new file mode 100644 index 00000000000..b38dd83b3bb --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/JaxrsRestServiceImpl.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.metadata.rest.api; + +import org.apache.dubbo.metadata.rest.User; + +import javax.ws.rs.core.MultivaluedMap; + +public class JaxrsRestServiceImpl implements JaxrsRestService { + + + @Override + public String param(String param) { + return param; + } + + @Override + public String header(String header) { + return header; + } + + @Override + public User body(User user) { + return user; + } + + @Override + public MultivaluedMap multiValue(MultivaluedMap map) { + return map; + } + + @Override + public String pathVariable(String a) { + return a; + } +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestService.java new file mode 100644 index 00000000000..00639e8c31e --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestService.java @@ -0,0 +1,52 @@ +/* + * 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.metadata.rest.api; + +import org.apache.dubbo.metadata.rest.User; +import org.springframework.http.MediaType; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.PathVariable; + + +@RestController +public interface SpringRestService { + + + @RequestMapping(value = "/param", method = RequestMethod.GET, consumes = MediaType.TEXT_PLAIN_VALUE, produces = MediaType.TEXT_PLAIN_VALUE) + String param(@RequestParam("param") String param); + + @RequestMapping(value = "/header", method = RequestMethod.GET, consumes = MediaType.TEXT_PLAIN_VALUE, produces = MediaType.TEXT_PLAIN_VALUE) + String header(@RequestHeader("header") String header); + + @RequestMapping(value = "/header", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + User body(@RequestBody User user); + + @RequestMapping(value = "/multiValue", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + MultiValueMap multiValue(@RequestBody MultiValueMap map); + + + @RequestMapping(value = "/pathVariable/{a}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + String pathVariable(@PathVariable String a); + + +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestServiceImpl.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestServiceImpl.java new file mode 100644 index 00000000000..8262d4fe663 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/api/SpringRestServiceImpl.java @@ -0,0 +1,53 @@ +/* + * 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.metadata.rest.api; + +import org.apache.dubbo.metadata.rest.User; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.RestController; + + +@RestController +public class SpringRestServiceImpl implements SpringRestService { + + + @Override + public String param(String param) { + return param; + } + + @Override + public String header(String header) { + return header; + } + + @Override + public User body(User user) { + return user; + } + + @Override + public MultiValueMap multiValue(MultiValueMap map) { + return map; + } + + @Override + public String pathVariable(String a) { + return a; + } + +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolverTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolverTest.java index fe18bbb5c46..98713cfd06c 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolverTest.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolverTest.java @@ -17,16 +17,27 @@ package org.apache.dubbo.metadata.rest.jaxrs; import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.metadata.rest.ClassPathServiceRestMetadataReader; import org.apache.dubbo.metadata.rest.DefaultRestService; +import org.apache.dubbo.metadata.rest.PathUtil; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; import org.apache.dubbo.metadata.rest.RestService; import org.apache.dubbo.metadata.rest.ServiceRestMetadata; -import org.apache.dubbo.metadata.rest.SpringRestService; import org.apache.dubbo.metadata.rest.StandardRestService; +import org.apache.dubbo.metadata.rest.api.AnotherUserRestService; +import org.apache.dubbo.metadata.rest.api.JaxrsRestService; +import org.apache.dubbo.metadata.rest.api.JaxrsRestServiceImpl; +import org.apache.dubbo.metadata.rest.api.SpringRestService; import org.apache.dubbo.rpc.model.ApplicationModel; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -42,6 +53,7 @@ class JAXRSServiceRestMetadataResolverTest { private JAXRSServiceRestMetadataResolver instance = new JAXRSServiceRestMetadataResolver(ApplicationModel.defaultModel()); + @Test void testSupports() { // JAX-RS RestService class @@ -57,6 +69,7 @@ void testSupports() { } @Test + @Disabled void testResolve() { // Generated by "dubbo-metadata-processor" ClassPathServiceRestMetadataReader reader = new ClassPathServiceRestMetadataReader("META-INF/dubbo/jax-rs-service-rest-metadata.json"); @@ -70,4 +83,75 @@ void testResolve() { assertEquals(expectedServiceRestMetadata, serviceRestMetadata); } + + @Test + void testResolves() { + testResolve(JaxrsRestService.class); + testResolve(JaxrsRestServiceImpl.class); + } + + + void testResolve(Class service) { + // Generated by "dubbo-metadata-processor" + + List jsons = Arrays.asList( + + "{\"argInfos\":[{\"annotationNameAttribute\":\"a\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"javax.ws.rs.PathParam\",\"paramName\":\"a\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":2}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"a\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/pathVariable/{a}\",\"produces\":[\"application/x-www-form-urlencoded\"]}}", + "{\"argInfos\":[{\"annotationNameAttribute\":\"map\",\"formContentType\":true,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestBody\",\"paramName\":\"map\",\"paramType\":\"javax.ws.rs.core.MultivaluedMap\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"map\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/multiValue\",\"produces\":[\"application/x-www-form-urlencoded\"]}}", + "{\"argInfos\":[{\"annotationNameAttribute\":\"user\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestBody\",\"paramName\":\"user\",\"paramType\":\"org.apache.dubbo.metadata.rest.User\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"user\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/body\",\"produces\":[\"application/json\"]}}", + "{\"argInfos\":[{\"annotationNameAttribute\":\"param\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"javax.ws.rs.QueryParam\",\"paramName\":\"param\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"param\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[],\"headers\":{},\"method\":\"GET\",\"paramNames\":[\"param\"],\"params\":{\"param\":[\"{0}\"]},\"path\":\"/param\",\"produces\":[\"text/plain\"]}}", + "{\"argInfos\":[{\"annotationNameAttribute\":\"header\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"javax.ws.rs.HeaderParam\",\"paramName\":\"header\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"header\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[\"header\"],\"headers\":{\"header\":[\"{0}\"]},\"method\":\"GET\",\"paramNames\":[],\"params\":{},\"path\":\"/header\",\"produces\":[\"text/plain\"]}}" + + + ); + + ServiceRestMetadata jaxrsRestMetadata = new ServiceRestMetadata(); + jaxrsRestMetadata.setServiceInterface(service.getName()); + ServiceRestMetadata jaxrsMetadata = instance.resolve(service, jaxrsRestMetadata); + + + List jsonsTmp = new ArrayList<>(); + for (RestMethodMetadata restMethodMetadata : jaxrsMetadata.getMeta()) { + restMethodMetadata.setServiceRestMetadata(null); + restMethodMetadata.setReflectMethod(null); + restMethodMetadata.setMethod(null); + jsonsTmp.add(JsonUtils.getJson().toJson(restMethodMetadata)); + } + + Comparator comparator = new Comparator() { + @Override + public int compare(String o1, String o2) { + return o1.length() - o2.length(); + } + }; + jsons.sort(comparator); + jsonsTmp.sort(comparator); + + + for (int i = 0; i < jsons.size(); i++) { + assertEquals(jsons.get(i), jsonsTmp.get(i)); + } + + } + + + @Test + public void testJaxrsPathPattern() { + Class service = AnotherUserRestService.class; + ServiceRestMetadata jaxrsRestMetadata = new ServiceRestMetadata(); + jaxrsRestMetadata.setServiceInterface(service.getName()); + ServiceRestMetadata jaxrsMetadata = instance.resolve(service, jaxrsRestMetadata); + + RestMethodMetadata[] objects = jaxrsMetadata.getMeta().toArray(new RestMethodMetadata[0]); + RestMethodMetadata object = null; + for (RestMethodMetadata obj : objects) { + if ("getUser".equals(obj.getReflectMethod().getName())) { + object = obj; + } + } + + + Assertions.assertEquals("/u/1", PathUtil.resolvePathVariable("u/{id : \\d+}", object.getArgInfos(), Arrays.asList(1))); + + } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java index 388030d4d94..b48c77ad1a9 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java @@ -17,16 +17,23 @@ package org.apache.dubbo.metadata.rest.springmvc; import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.metadata.rest.ClassPathServiceRestMetadataReader; import org.apache.dubbo.metadata.rest.DefaultRestService; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; import org.apache.dubbo.metadata.rest.RestService; import org.apache.dubbo.metadata.rest.ServiceRestMetadata; -import org.apache.dubbo.metadata.rest.SpringRestService; import org.apache.dubbo.metadata.rest.StandardRestService; +import org.apache.dubbo.metadata.rest.api.SpringRestService; +import org.apache.dubbo.metadata.rest.api.SpringRestServiceImpl; import org.apache.dubbo.rpc.model.ApplicationModel; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -45,18 +52,19 @@ class SpringMvcServiceRestMetadataResolverTest { @Test void testSupports() { // Spring MVC RestService class - assertTrue(instance.supports(SpringRestService.class)); + assertTrue(instance.supports(SpringRestService.class, true)); // JAX-RS RestService class - assertFalse(instance.supports(StandardRestService.class)); + assertFalse(instance.supports(StandardRestService.class, true)); // Default RestService class - assertFalse(instance.supports(DefaultRestService.class)); + assertFalse(instance.supports(DefaultRestService.class, true)); // No annotated RestService class - assertFalse(instance.supports(RestService.class)); + assertFalse(instance.supports(RestService.class, true)); // null - assertFalse(instance.supports(null)); + assertFalse(instance.supports(null, true)); } @Test + @Disabled void testResolve() { // Generated by "dubbo-metadata-processor" ClassPathServiceRestMetadataReader reader = new ClassPathServiceRestMetadataReader("META-INF/dubbo/spring-mvc-service-rest-metadata.json"); @@ -70,4 +78,51 @@ void testResolve() { assertEquals(expectedServiceRestMetadata, serviceRestMetadata); } + + @Test + void testResolves() { + testResolve(SpringRestService.class); + testResolve(SpringRestServiceImpl.class); + } + + void testResolve(Class service) { + List jsons = Arrays.asList( + + "{\"argInfos\":[{\"annotationNameAttribute\":\"\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.PathVariable\",\"paramName\":\"a\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":2}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"a\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/pathVariable/{a}\",\"produces\":[\"application/x-www-form-urlencoded\"]}}", + "{\"argInfos\":[{\"annotationNameAttribute\":\"user\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestBody\",\"paramName\":\"user\",\"paramType\":\"org.apache.dubbo.metadata.rest.User\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"user\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/header\",\"produces\":[\"application/json\"]}}", + "{\"argInfos\":[{\"annotationNameAttribute\":\"param\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestParam\",\"paramName\":\"param\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"param\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[],\"headers\":{},\"method\":\"GET\",\"paramNames\":[\"param\"],\"params\":{\"param\":[\"{0}\"]},\"path\":\"/param\",\"produces\":[\"text/plain\"]}}", + "{\"argInfos\":[{\"annotationNameAttribute\":\"map\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestBody\",\"paramName\":\"map\",\"paramType\":\"org.springframework.util.MultiValueMap\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"map\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/multiValue\",\"produces\":[\"application/x-www-form-urlencoded\"]}}", + "{\"argInfos\":[{\"annotationNameAttribute\":\"header\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestHeader\",\"paramName\":\"header\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"header\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[\"header\"],\"headers\":{\"header\":[\"{0}\"]},\"method\":\"GET\",\"paramNames\":[],\"params\":{},\"path\":\"/header\",\"produces\":[\"text/plain\"]}}" + + + ); + + ServiceRestMetadata springRestMetadata = new ServiceRestMetadata(); + springRestMetadata.setServiceInterface(service.getName()); + ServiceRestMetadata springMetadata = instance.resolve(service, springRestMetadata); + + + List jsonsTmp = new ArrayList<>(); + for (RestMethodMetadata restMethodMetadata : springMetadata.getMeta()) { + restMethodMetadata.setServiceRestMetadata(null); + restMethodMetadata.setReflectMethod(null); + restMethodMetadata.setMethod(null); + jsonsTmp.add(JsonUtils.getJson().toJson(restMethodMetadata)); + } + + Comparator comparator = new Comparator() { + @Override + public int compare(String o1, String o2) { + return o1.length() - o2.length(); + } + }; + jsons.sort(comparator); + jsonsTmp.sort(comparator); + + + for (int i = 0; i < jsons.size(); i++) { + assertEquals(jsons.get(i), jsonsTmp.get(i)); + } + + } } diff --git a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/filter/MetricsFilterTest.java b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/filter/MetricsFilterTest.java index ea67b53677e..f18115607ff 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/filter/MetricsFilterTest.java +++ b/dubbo-metrics/dubbo-metrics-default/src/test/java/org/apache/dubbo/metrics/filter/MetricsFilterTest.java @@ -17,12 +17,6 @@ package org.apache.dubbo.metrics.filter; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.config.ApplicationConfig; @@ -31,13 +25,25 @@ import org.apache.dubbo.metrics.model.MetricsKey; import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; import org.apache.dubbo.metrics.model.sample.MetricSample; -import org.apache.dubbo.rpc.*; +import org.apache.dubbo.rpc.AppResponse; +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.RpcInvocation; import org.apache.dubbo.rpc.model.ApplicationModel; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_PARAMETER_DESC; import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY; diff --git a/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterTest.java b/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterTest.java index 4f0dab159ba..08186127ec9 100644 --- a/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterTest.java +++ b/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterTest.java @@ -17,7 +17,6 @@ package org.apache.dubbo.metrics.prometheus; -import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.MetricsConfig; import org.apache.dubbo.config.nested.PrometheusConfig; diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/DefaultCommandExecutorTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/DefaultCommandExecutorTest.java index 90e0cacd3e4..7fd82f63ca5 100644 --- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/DefaultCommandExecutorTest.java +++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/DefaultCommandExecutorTest.java @@ -18,11 +18,9 @@ package org.apache.dubbo.qos.command; import org.apache.dubbo.qos.command.exception.NoSuchCommandException; -import org.apache.dubbo.qos.command.exception.PermissionDenyException; import org.apache.dubbo.qos.common.QosConfiguration; import org.apache.dubbo.qos.permission.PermissionLevel; import org.apache.dubbo.rpc.model.FrameworkModel; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMappingTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMappingTest.java index b34928fb415..03adb56fbc3 100644 --- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMappingTest.java +++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMappingTest.java @@ -31,9 +31,7 @@ import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.metadata.report.MetadataReportInstance; import org.apache.dubbo.rpc.model.ApplicationModel; -import org.apache.dubbo.rpc.model.FrameworkModel; -import org.checkerframework.checker.units.qual.A; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Constants.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Constants.java index d6cd9f6e121..df8e5dcb454 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Constants.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Constants.java @@ -110,9 +110,9 @@ public interface Constants { int DEFAULT_IO_THREADS = Math.min(Runtime.getRuntime().availableProcessors() + 1, 32); - String EVENT_LOOP_BOSS_POOL_NAME = "NettyServerBoss"; + String EVENT_LOOP_BOSS_POOL_NAME = "NettyServerBoss"; - String EVENT_LOOP_WORKER_POOL_NAME = "NettyServerWorker"; + String EVENT_LOOP_WORKER_POOL_NAME = "NettyServerWorker"; String NETTY_EPOLL_ENABLE_KEY = "netty.epoll.enable"; @@ -148,4 +148,14 @@ public interface Constants { String CONNECTIONS_KEY = "connections"; int DEFAULT_BACKLOG = 1024; + + String CONNECTION = "Connection"; + + String KEEP_ALIVE = "keep-alive"; + + String KEEP_ALIVE_HEADER = "Keep-Alive"; + + String OK_HTTP = "ok-http"; + String URL_CONNECTION = "url-connection"; + String APACHE_HTTP_CLIENT = "apache-http-client"; } diff --git a/dubbo-remoting/dubbo-remoting-http/pom.xml b/dubbo-remoting/dubbo-remoting-http/pom.xml index d613bd93be1..b2ef9653a4b 100644 --- a/dubbo-remoting/dubbo-remoting-http/pom.xml +++ b/dubbo-remoting/dubbo-remoting-http/pom.xml @@ -59,5 +59,23 @@ 4.5.14 test + + + com.squareup.okhttp3 + okhttp + + + + org.apache.dubbo + dubbo-rpc-api + ${project.parent.version} + + + + org.apache.httpcomponents + httpclient + + + - \ No newline at end of file + diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/BaseRestClient.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/BaseRestClient.java new file mode 100644 index 00000000000..d644daf7dd1 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/BaseRestClient.java @@ -0,0 +1,43 @@ +/* + * 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.remoting.http; + + +import org.apache.dubbo.remoting.http.config.HttpClientConfig; + +public abstract class BaseRestClient implements RestClient { + + protected CLIENT client; + + protected HttpClientConfig clientConfig; + + public BaseRestClient(HttpClientConfig clientConfig) { + this.clientConfig = clientConfig; + client = createHttpClient(clientConfig); + } + + protected abstract CLIENT createHttpClient(HttpClientConfig clientConfig); + + + public HttpClientConfig getClientConfig() { + return clientConfig; + } + + public CLIENT getClient() { + return client; + } +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RequestTemplate.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RequestTemplate.java new file mode 100644 index 00000000000..e5d644c90b0 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RequestTemplate.java @@ -0,0 +1,303 @@ +/* + * 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.remoting.http; + + +import org.apache.dubbo.remoting.Constants; +import org.apache.dubbo.rpc.Invocation; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + + +public class RequestTemplate implements Serializable { + private static final long serialVersionUID = 1L; + public static final String CONTENT_ENCODING = "Content-Encoding"; + private static final String CONTENT_LENGTH = "Content-Length"; + public static final String ENCODING_GZIP = "gzip"; + public static final String ENCODING_DEFLATE = "deflate"; + private static final List EMPTY_ARRAYLIST = new ArrayList<>(); + + private final Map> queries = new LinkedHashMap>(); + private final Map> headers = new LinkedHashMap>(); + private String httpMethod; + private String path; + private String address; + private Object body; + private byte[] byteBody = new byte[0]; + private String protocol = "http://"; + private final Invocation invocation; + private String contextPath = ""; + + + public RequestTemplate(Invocation invocation, String httpMethod, String address) { + this(invocation, httpMethod, address, ""); + } + + public RequestTemplate(Invocation invocation, String httpMethod, String address, String contextPath) { + this.httpMethod = httpMethod; + this.address = address; + this.invocation = invocation; + this.contextPath = contextPath; + } + + public String getURL() { + StringBuilder stringBuilder = new StringBuilder(getProtocol() + address); + + stringBuilder.append(getUri()); + return stringBuilder.toString(); + } + + public String getUri() { + StringBuilder stringBuilder = new StringBuilder(getContextPath() + path); + return stringBuilder.append(getQueryString()).toString(); + } + + public String getQueryString() { + + if (queries.isEmpty()) { + return ""; + } + + StringBuilder queryBuilder = new StringBuilder("?"); + for (String field : queries.keySet()) { + + Collection queryValues = queries.get(field); + + if (queryValues == null || queryValues.isEmpty()) { + continue; + } + + for (String value : queryValues) { + queryBuilder.append('&'); + queryBuilder.append(field); + if (value == null) { + continue; + } + + queryBuilder.append('='); + queryBuilder.append(value); + } + } + + return queryBuilder.toString(); + + } + + + public RequestTemplate path(String path) { + this.path = path; + return this; + } + + public String getHttpMethod() { + return httpMethod; + } + + public RequestTemplate httpMethod(String httpMethod) { + this.httpMethod = httpMethod; + return this; + } + + public byte[] getSerializedBody() { + return byteBody; + } + + public void serializeBody(byte[] body) { + addHeader(CONTENT_LENGTH, body.length); // must header + this.byteBody = body; + } + + public boolean isBodyEmpty() { + return getUnSerializedBody() == null; + } + + public RequestTemplate body(Object body) { + this.body = body; + return this; + } + + public Object getUnSerializedBody() { + return body; + } + + public Map> getAllHeaders() { + return headers; + } + + public Collection getHeaders(String name) { + return headers.get(name); + } + + public String getHeader(String name) { + if (headers.containsKey(name)) { + + Collection headers = getHeaders(name); + + if (headers.isEmpty()) { + return null; + } + String[] strings = headers.toArray(new String[0]); + return strings[0]; + + } else { + return null; + } + } + + public Collection getEncodingValues() { + if (headers.containsKey(CONTENT_ENCODING)) { + return headers.get(CONTENT_ENCODING); + } + return EMPTY_ARRAYLIST; + } + + public boolean isGzipEncodedRequest() { + return getEncodingValues().contains(ENCODING_GZIP); + } + + public boolean isDeflateEncodedRequest() { + return getEncodingValues().contains(ENCODING_DEFLATE); + } + + public void addHeader(String key, String value) { + addValueByKey(key, value, this.headers); + } + + public void addHeader(String key, Object value) { + addValueByKey(key, String.valueOf(value), this.headers); + } + + public void addKeepAliveHeader(int time) { + addHeader(Constants.KEEP_ALIVE_HEADER, time); + addHeader(Constants.CONNECTION, Constants.KEEP_ALIVE); + } + + public void addHeaders(String key, Collection values) { + Collection header = getHeaders(key); + + if (header == null) { + header = new HashSet<>(); + this.headers.put(key, header); + } + header.addAll(values); + } + + + public void addParam(String key, String value) { + addValueByKey(key, value, this.queries); + } + + public void addParam(String key, Object value) { + addParam(key, String.valueOf(value)); + } + + public Map> getQueries() { + return queries; + } + + public Collection getParam(String key) { + return getQueries().get(key); + } + + public void addParams(String key, Collection values) { + Collection params = getParam(key); + + if (params == null) { + params = new HashSet<>(); + this.headers.put(key, params); + } + params.addAll(values); + } + + + public void addValueByKey(String key, String value, Map> maps) { + + if (value == null) { + return; + } + + Collection values = null; + if (!maps.containsKey(key)) { + values = new HashSet<>(); + maps.put(key, values); + } + values = maps.get(key); + + + values.add(value); + + } + + + public Integer getContentLength() { + + if (!getAllHeaders().containsKey(CONTENT_LENGTH)) { + return null; + } + + HashSet strings = (HashSet) getAllHeaders().get(CONTENT_LENGTH); + + return Integer.parseInt(new ArrayList<>(strings).get(0)); + + } + + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + addHeader("Host", address);// must header + this.address = address; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + + public Invocation getInvocation() { + return invocation; + } + + public String getContextPath() { + if (contextPath == null || contextPath.length() == 0) { + return ""; + } + + if (contextPath.startsWith("/")) { + return contextPath; + } else { + return "/" + contextPath; + } + + } + + public void setContextPath(String contextPath) { + this.contextPath = contextPath; + } +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestClient.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestClient.java new file mode 100644 index 00000000000..f8827dd2d47 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestClient.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.remoting.http; + +import org.apache.dubbo.remoting.RemotingException; + +import java.util.concurrent.CompletableFuture; + + +public interface RestClient { + /** + * send message. + * + * @param message + * @throws RemotingException + */ + CompletableFuture send(RequestTemplate message); + + /** + * close the channel. + */ + void close(); + + /** + * Graceful close the channel. + */ + void close(int timeout); + + /** + * is closed. + * + * @return closed + */ + boolean isClosed(); + +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestResult.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestResult.java new file mode 100644 index 00000000000..1cca8292914 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestResult.java @@ -0,0 +1,35 @@ +/* + * 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.remoting.http; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public interface RestResult { + String getContentType(); + + byte[] getBody() throws IOException; + + Map> headers(); + + byte[] getErrorResponse() throws IOException; + + int getResponseCode() throws IOException; + + String getMessage() throws IOException; +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/config/HttpClientConfig.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/config/HttpClientConfig.java new file mode 100644 index 00000000000..6e7e49e660f --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/config/HttpClientConfig.java @@ -0,0 +1,62 @@ +/* + * 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.remoting.http.config; + +public class HttpClientConfig { + private int readTimeout = 6 * 1000; + private int writeTimeout = 6 * 1000; + private int connectTimeout = 6 * 1000; + private int chunkLength = 8196; + + private int HTTP_CLIENT_CONNECTION_MANAGER_MAX_PER_ROUTE = 20; + private int HTTP_CLIENT_CONNECTION_MANAGER_MAX_TOTAL = 20; + private int HTTPCLIENT_KEEP_ALIVE_DURATION = 30 * 1000; + private int HTTP_CLIENT_CONNECTION_MANAGER_CLOSE_WAIT_TIME_MS = 1000; + private int HTTP_CLIENT_CONNECTION_MANAGER_CLOSE_IDLE_TIME_S = 30; + + + public HttpClientConfig() { + } + + public int getReadTimeout() { + return readTimeout; + } + + public void setReadTimeout(int readTimeout) { + this.readTimeout = readTimeout; + } + + public int getWriteTimeout() { + return writeTimeout; + } + + public void setWriteTimeout(int writeTimeout) { + this.writeTimeout = writeTimeout; + } + + public int getConnectTimeout() { + return connectTimeout; + } + + public void setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + } + + public int getChunkLength() { + return chunkLength; + } +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/AbstractHttpClientFactory.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/AbstractHttpClientFactory.java new file mode 100644 index 00000000000..44936b0d37f --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/AbstractHttpClientFactory.java @@ -0,0 +1,87 @@ +/* + * 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. + */ +/* + * 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.remoting.http.factory; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.remoting.http.RestClient; +import org.apache.dubbo.remoting.http.config.HttpClientConfig; +import org.apache.dubbo.rpc.RpcException; + +/** + * AbstractHttpClientFactory + */ +public abstract class AbstractHttpClientFactory implements RestClientFactory { + + protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); + + + // TODO load config + protected HttpClientConfig httpClientConfig = new HttpClientConfig(); + + //////////////////////////////////////// implements start /////////////////////////////////////////////// + @Override + public RestClient createRestClient(URL url) throws RpcException { + + beforeCreated(url); + + // create a raw client + RestClient restClient = doCreateRestClient(url); + + // postprocessor + afterCreated(restClient); + + return restClient; + } + + //////////////////////////////////////// implements end /////////////////////////////////////////////// + + + //////////////////////////////////////// inner methods /////////////////////////////////////////////// + + protected void beforeCreated(URL url) { + } + + protected abstract RestClient doCreateRestClient(URL url) throws RpcException; + + protected void afterCreated(RestClient client) { + } + + //////////////////////////////////////// inner methods /////////////////////////////////////////////// + + + + + +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/RestClientFactory.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/RestClientFactory.java new file mode 100644 index 00000000000..ca82b052e50 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/RestClientFactory.java @@ -0,0 +1,38 @@ +/* + * 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.remoting.http.factory; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.Adaptive; +import org.apache.dubbo.common.extension.ExtensionScope; +import org.apache.dubbo.common.extension.SPI; +import org.apache.dubbo.remoting.Constants; +import org.apache.dubbo.remoting.http.RestClient; +import org.apache.dubbo.rpc.RpcException; + +/** + * RestClientFactory. (API/SPI, Singleton, ThreadSafe) + */ +@SPI(value = Constants.OK_HTTP, scope = ExtensionScope.FRAMEWORK) +public interface RestClientFactory { + + + @Adaptive({Constants.CLIENT_KEY}) + RestClient createRestClient(URL url) throws RpcException; + + +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/ApacheHttpClientFactory.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/ApacheHttpClientFactory.java new file mode 100644 index 00000000000..08fef6f4ea4 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/ApacheHttpClientFactory.java @@ -0,0 +1,37 @@ +/* + * 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.remoting.http.factory.impl; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.remoting.Constants; +import org.apache.dubbo.remoting.http.RestClient; +import org.apache.dubbo.remoting.http.factory.AbstractHttpClientFactory; +import org.apache.dubbo.remoting.http.restclient.HttpClientRestClient; +import org.apache.dubbo.rpc.RpcException; + +@Activate(Constants.APACHE_HTTP_CLIENT) +public class ApacheHttpClientFactory extends AbstractHttpClientFactory { + + + @Override + protected RestClient doCreateRestClient(URL url) throws RpcException { + + + return new HttpClientRestClient(httpClientConfig); + } +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/OkHttpClientFactory.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/OkHttpClientFactory.java new file mode 100644 index 00000000000..87e154c76a4 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/OkHttpClientFactory.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting.http.factory.impl; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.remoting.Constants; +import org.apache.dubbo.remoting.http.RestClient; +import org.apache.dubbo.remoting.http.factory.AbstractHttpClientFactory; +import org.apache.dubbo.remoting.http.restclient.OKHttpRestClient; +import org.apache.dubbo.rpc.RpcException; + +@Activate(Constants.OK_HTTP) +public class OkHttpClientFactory extends AbstractHttpClientFactory { + + + @Override + protected RestClient doCreateRestClient(URL url) throws RpcException { + + return new OKHttpRestClient(httpClientConfig); + } +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/URLConnectionClientFactory.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/URLConnectionClientFactory.java new file mode 100644 index 00000000000..94676a091d2 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/URLConnectionClientFactory.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting.http.factory.impl; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.remoting.Constants; +import org.apache.dubbo.remoting.http.RestClient; +import org.apache.dubbo.remoting.http.factory.AbstractHttpClientFactory; +import org.apache.dubbo.remoting.http.restclient.URLConnectionRestClient; +import org.apache.dubbo.rpc.RpcException; + +@Activate(Constants.URL_CONNECTION) +public class URLConnectionClientFactory extends AbstractHttpClientFactory { + + + @Override + protected RestClient doCreateRestClient(URL url) throws RpcException { + + return new URLConnectionRestClient(httpClientConfig); + } +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java new file mode 100644 index 00000000000..9895eab46a9 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java @@ -0,0 +1,153 @@ +/* + * 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.remoting.http.restclient; + +import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.remoting.http.RestClient; +import org.apache.dubbo.remoting.http.RestResult; +import org.apache.dubbo.remoting.http.config.HttpClientConfig; + +import org.apache.commons.io.IOUtils; +import org.apache.http.Header; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + + +public class HttpClientRestClient implements RestClient { + private final CloseableHttpClient closeableHttpClient; + private final HttpClientConfig httpClientConfig; + + public HttpClientRestClient(HttpClientConfig clientConfig) { + closeableHttpClient = createHttpClient(); + httpClientConfig = clientConfig; + } + + @Override + public CompletableFuture send(RequestTemplate requestTemplate) { + + HttpRequestBase httpRequest = null; + String httpMethod = requestTemplate.getHttpMethod(); + + if ("GET".equals(httpMethod)) { + httpRequest = new HttpGet(requestTemplate.getURL()); + } else if ("POST".equals(httpMethod)) { + HttpPost httpPost = new HttpPost(requestTemplate.getURL()); + httpPost.setEntity(new ByteArrayEntity(requestTemplate.getSerializedBody())); + httpRequest = httpPost; + } + + Map> allHeaders = requestTemplate.getAllHeaders(); + + allHeaders.remove("Content-Length"); + // header + for (String headerName : allHeaders.keySet()) { + Collection headerValues = allHeaders.get(headerName); + + for (String headerValue : headerValues) { + httpRequest.addHeader(headerName, headerValue); + } + } + + httpRequest.setConfig(getRequestConfig(httpClientConfig)); + + CompletableFuture future = new CompletableFuture<>(); + try { + CloseableHttpResponse response = closeableHttpClient.execute(httpRequest); + future.complete(new RestResult() { + @Override + public String getContentType() { + return response.getFirstHeader("Content-Type").getValue(); + } + + @Override + public byte[] getBody() throws IOException { + return IOUtils.toByteArray(response.getEntity().getContent()); + } + + @Override + public Map> headers() { + return Arrays.stream(response.getAllHeaders()).collect(Collectors.toMap(Header::getName, h -> Collections.singletonList(h.getValue()))); + } + + @Override + public byte[] getErrorResponse() throws IOException { + return getBody(); + } + + @Override + public int getResponseCode() { + return response.getStatusLine().getStatusCode(); + } + + @Override + public String getMessage() throws IOException { + return response.getStatusLine().getReasonPhrase(); + } + }); + } catch (IOException e) { + future.completeExceptionally(e); + } + return future; + } + + private RequestConfig getRequestConfig(HttpClientConfig clientConfig) { + + // TODO config + return RequestConfig.custom().build(); + } + + @Override + public void close() { + try { + closeableHttpClient.close(); + } catch (IOException e) { + + } + } + + @Override + public void close(int timeout) { + + } + + @Override + public boolean isClosed() { + // TODO close judge + return true; + } + + public CloseableHttpClient createHttpClient() { + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); + return HttpClients.custom().setConnectionManager(connectionManager).build(); + } +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/OKHttpRestClient.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/OKHttpRestClient.java new file mode 100644 index 00000000000..bb98aa32ece --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/OKHttpRestClient.java @@ -0,0 +1,144 @@ +/* + * 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.remoting.http.restclient; + +import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.remoting.http.RestClient; +import org.apache.dubbo.remoting.http.RestResult; +import org.apache.dubbo.remoting.http.config.HttpClientConfig; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.internal.http.HttpMethod; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + + +public class OKHttpRestClient implements RestClient { + private final OkHttpClient okHttpClient; + private final HttpClientConfig httpClientConfig; + + public OKHttpRestClient(HttpClientConfig clientConfig) { + this.okHttpClient = createHttpClient(clientConfig); + this.httpClientConfig = clientConfig; + } + + @Override + public CompletableFuture send(RequestTemplate requestTemplate) { + + Request.Builder builder = new Request.Builder(); + // url + builder.url(requestTemplate.getURL()); + + Map> allHeaders = requestTemplate.getAllHeaders(); + + // header + for (String headerName : allHeaders.keySet()) { + Collection headerValues = allHeaders.get(headerName); + + for (String headerValue : headerValues) { + builder.addHeader(headerName, headerValue); + } + } + RequestBody requestBody = null; + if (HttpMethod.permitsRequestBody(requestTemplate.getHttpMethod())) { + requestBody = RequestBody.create(null, requestTemplate.getSerializedBody()); + } + builder.method(requestTemplate.getHttpMethod(), requestBody); + + CompletableFuture future = new CompletableFuture<>(); + + okHttpClient.newCall(builder.build()).enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + future.completeExceptionally(e); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + future.complete(new RestResult() { + @Override + public String getContentType() { + return response.header("Content-Type"); + } + + @Override + public byte[] getBody() throws IOException { + ResponseBody body = response.body(); + return body == null ? null : body.bytes(); + } + + @Override + public Map> headers() { + return response.headers().toMultimap(); + } + + @Override + public byte[] getErrorResponse() throws IOException { + return getBody(); + } + + @Override + public int getResponseCode() throws IOException { + return response.code(); + } + + @Override + public String getMessage() throws IOException { + return response.message(); + } + }); + } + }); + + return future; + } + + @Override + public void close() { + okHttpClient.connectionPool().evictAll(); + } + + @Override + public void close(int timeout) { + + } + + @Override + public boolean isClosed() { + return okHttpClient.retryOnConnectionFailure(); + } + + public OkHttpClient createHttpClient(HttpClientConfig httpClientConfig) { + OkHttpClient client = new OkHttpClient.Builder(). + readTimeout(httpClientConfig.getReadTimeout(), TimeUnit.SECONDS). + writeTimeout(httpClientConfig.getWriteTimeout(), TimeUnit.SECONDS). + connectTimeout(httpClientConfig.getConnectTimeout(), TimeUnit.SECONDS). + build(); + return client; + } +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/URLConnectionRestClient.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/URLConnectionRestClient.java new file mode 100644 index 00000000000..b067228c44d --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/URLConnectionRestClient.java @@ -0,0 +1,181 @@ +/* + * 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.remoting.http.restclient; + +import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.remoting.http.RestClient; +import org.apache.dubbo.remoting.http.RestResult; +import org.apache.dubbo.remoting.http.config.HttpClientConfig; + +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPOutputStream; + + +public class URLConnectionRestClient implements RestClient { + private final HttpClientConfig clientConfig; + + public URLConnectionRestClient(HttpClientConfig clientConfig) { + this.clientConfig = clientConfig; + } + + @Override + public CompletableFuture send(RequestTemplate requestTemplate) { + + CompletableFuture future = new CompletableFuture<>(); + + try { + HttpURLConnection connection = (HttpURLConnection) new URL(requestTemplate.getURL()).openConnection(); + connection.setConnectTimeout(clientConfig.getConnectTimeout()); + connection.setReadTimeout(clientConfig.getReadTimeout()); + connection.setAllowUserInteraction(false); + connection.setInstanceFollowRedirects(true); + connection.setRequestMethod(requestTemplate.getHttpMethod()); + + // writeHeaders + + for (String field : requestTemplate.getAllHeaders().keySet()) { + for (String value : requestTemplate.getHeaders(field)) { + connection.addRequestProperty(field, value); + } + } + + + // writeBody + + boolean gzipEncodedRequest = requestTemplate.isGzipEncodedRequest(); + boolean deflateEncodedRequest = requestTemplate.isDeflateEncodedRequest(); + if (requestTemplate.isBodyEmpty()) { + future.complete(new RestResult() { + @Override + public String getContentType() { + return connection.getContentType(); + } + + @Override + public byte[] getBody() throws IOException { + return IOUtils.toByteArray(connection.getInputStream()); + } + + @Override + public Map> headers() { + return connection.getHeaderFields(); + } + + @Override + public byte[] getErrorResponse() throws IOException { + return IOUtils.toByteArray(connection.getErrorStream()); + } + + @Override + public int getResponseCode() throws IOException { + return connection.getResponseCode(); + } + + @Override + public String getMessage() throws IOException { + return connection.getResponseMessage(); + } + }); + return future; + } + Integer contentLength = requestTemplate.getContentLength(); + + if (contentLength != null) { + connection.setFixedLengthStreamingMode(contentLength); + } else { + connection.setChunkedStreamingMode(clientConfig.getChunkLength()); + } + connection.setDoOutput(true); + + OutputStream out = connection.getOutputStream(); + if (gzipEncodedRequest) { + out = new GZIPOutputStream(out); + } else if (deflateEncodedRequest) { + out = new DeflaterOutputStream(out); + } + try { + out.write(requestTemplate.getSerializedBody()); + } finally { + try { + out.close(); + } catch (IOException suppressed) { + } + } + + future.complete(new RestResult() { + @Override + public String getContentType() { + return connection.getContentType(); + } + + @Override + public byte[] getBody() throws IOException { + return IOUtils.toByteArray(connection.getInputStream()); + } + + @Override + public Map> headers() { + return connection.getHeaderFields(); + } + + @Override + public byte[] getErrorResponse() throws IOException { + return IOUtils.toByteArray(connection.getErrorStream()); + } + + @Override + public int getResponseCode() throws IOException { + return connection.getResponseCode(); + } + + @Override + public String getMessage() throws IOException { + return connection.getResponseMessage(); + } + }); + } catch (Exception e) { + future.completeExceptionally(e); + } + + return future; + } + + @Override + public void close() { + + } + + @Override + public void close(int timeout) { + + } + + @Override + public boolean isClosed() { + return true; + } + +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.factory.RestClientFactory b/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.factory.RestClientFactory new file mode 100644 index 00000000000..95dd12e072d --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.factory.RestClientFactory @@ -0,0 +1,4 @@ +ok-http=org.apache.dubbo.remoting.http.factory.impl.OkHttpClientFactory +url-connection=org.apache.dubbo.remoting.http.factory.impl.URLConnectionClientFactory +apache-http-client=org.apache.dubbo.remoting.http.factory.impl.ApacheHttpClientFactory + diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/MultiplexProtocolConnectionManagerTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/MultiplexProtocolConnectionManagerTest.java index 0e31a128fed..634c4a72c16 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/MultiplexProtocolConnectionManagerTest.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/api/MultiplexProtocolConnectionManagerTest.java @@ -29,7 +29,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.opentest4j.AssertionFailedError; import java.lang.reflect.Field; import java.util.Map; diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java index 93285980819..360e629c729 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java @@ -45,19 +45,14 @@ import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; -import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNSUPPORTED; -import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; /** * AbstractProxyProtocol */ public abstract class AbstractProxyProtocol extends AbstractProtocol { - private final List> rpcExceptions = new CopyOnWriteArrayList>(); + protected final List> rpcExceptions = new CopyOnWriteArrayList>(); protected ProxyFactory proxyFactory; @@ -139,48 +134,6 @@ public void afterUnExport() { return exporter; } - @Override - protected Invoker protocolBindingRefer(final Class type, final URL url) throws RpcException { - final Invoker target = proxyFactory.getInvoker(doRefer(type, url), type, url); - Invoker invoker = new AbstractInvoker(type, url, new String[]{INTERFACE_KEY, GROUP_KEY, TOKEN_KEY}) { - @Override - protected Result doInvoke(Invocation invocation) { - try { - invocation.setAttachment(PATH_KEY, getUrl().getPath()); - invocation.setAttachment(VERSION_KEY, version); - Result result = target.invoke(invocation); - // FIXME result is an AsyncRpcResult instance. - Throwable e = result.getException(); - if (e != null) { - for (Class rpcException : rpcExceptions) { - if (rpcException.isAssignableFrom(e.getClass())) { - throw getRpcException(type, url, invocation, e); - } - } - } - return result; - } catch (RpcException e) { - if (e.getCode() == RpcException.UNKNOWN_EXCEPTION) { - e.setCode(getErrorCode(e.getCause())); - } - throw e; - } catch (Throwable e) { - throw getRpcException(type, url, invocation, e); - } - } - - @Override - public void destroy() { - super.destroy(); - target.destroy(); - invokers.remove(this); - AbstractProxyProtocol.this.destroyInternal(url); - } - }; - invokers.add(invoker); - return invoker; - } - // used to destroy unused clients and other resource protected void destroyInternal(URL url) { // subclass override @@ -207,8 +160,6 @@ protected int getErrorCode(Throwable e) { protected abstract Runnable doExport(T impl, Class type, URL url) throws RpcException; - protected abstract T doRefer(Class type, URL url) throws RpcException; - protected class ProxyProtocolServer implements ProtocolServer { private RemotingServer server; diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java index b6ceac83425..e6d26ff8fe4 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java @@ -18,11 +18,9 @@ import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.url.component.URLParam; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.io.InputStream; import java.util.Collection; import java.util.HashMap; import java.util.Map; diff --git a/dubbo-rpc/dubbo-rpc-rest/pom.xml b/dubbo-rpc/dubbo-rpc-rest/pom.xml index 8f94d12c65c..dc5855b88af 100644 --- a/dubbo-rpc/dubbo-rpc-rest/pom.xml +++ b/dubbo-rpc/dubbo-rpc-rest/pom.xml @@ -16,7 +16,7 @@ --> 4.0.0 - + org.apache.dubbo dubbo-rpc ${revision} @@ -112,10 +112,48 @@ test + + + org.apache.dubbo + dubbo-metadata-api + ${project.parent.version} + + + + javax.servlet + javax.servlet-api + provided + + + + jakarta.servlet + jakarta.servlet-api + provided + + + + io.netty + netty-codec-http + provided + + io.netty netty-transport compile + + + + org.springframework + spring-web + test + + + + org.springframework + spring-context + test + diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/NettyRestProtocolServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/NettyRestProtocolServer.java index 918f10afda7..b066c79f43e 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/NettyRestProtocolServer.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/NettyRestProtocolServer.java @@ -16,16 +16,16 @@ */ package org.apache.dubbo.rpc.protocol.rest; -import io.netty.channel.ChannelOption; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; + +import io.netty.channel.ChannelOption; import org.jboss.resteasy.plugins.server.netty.NettyJaxrsServer; import org.jboss.resteasy.spi.ResteasyDeployment; import java.util.HashMap; import java.util.Map; - import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREADS; import static org.apache.dubbo.common.constants.CommonConstants.IO_THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY; diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/ReferenceCountedClient.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/ReferenceCountedClient.java index 393a6d6c341..fc8d4664c8a 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/ReferenceCountedClient.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/ReferenceCountedClient.java @@ -19,32 +19,31 @@ import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.reference.ReferenceCountedResource; - -import org.jboss.resteasy.client.jaxrs.ResteasyClient; +import org.apache.dubbo.remoting.http.RestClient; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_CLIENT; -public class ReferenceCountedClient extends ReferenceCountedResource { +public class ReferenceCountedClient extends ReferenceCountedResource { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ReferenceCountedClient.class); - private final ResteasyClient resteasyClient; + private T client; - public ReferenceCountedClient(ResteasyClient resteasyClient) { - this.resteasyClient = resteasyClient; + public ReferenceCountedClient(T client) { + this.client = client; } - public ResteasyClient getClient() { - return resteasyClient; + public T getClient() { + return client; } public boolean isDestroyed() { - return resteasyClient.isClosed(); + return client.isClosed(); } @Override protected void destroy() { try { - resteasyClient.close(); + client.close(); } catch (Exception e) { logger.error(PROTOCOL_ERROR_CLOSE_CLIENT, "", "", "Close resteasy client error", e); } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java index 1966e7f0ec6..90e254be712 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java @@ -19,72 +19,73 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; import org.apache.dubbo.common.utils.StringUtils; -import org.apache.dubbo.remoting.http.HttpBinder; +import org.apache.dubbo.metadata.ParameterTypesComparator; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.remoting.http.RestClient; +import org.apache.dubbo.remoting.http.RestResult; +import org.apache.dubbo.remoting.http.factory.RestClientFactory; import org.apache.dubbo.remoting.http.servlet.BootstrapListener; import org.apache.dubbo.remoting.http.servlet.ServletManager; +import org.apache.dubbo.rpc.AppResponse; +import org.apache.dubbo.rpc.AsyncRpcResult; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.ProtocolServer; +import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.model.FrameworkModel; +import org.apache.dubbo.rpc.protocol.AbstractInvoker; import org.apache.dubbo.rpc.protocol.AbstractProxyProtocol; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionConfig; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept; +import org.apache.dubbo.rpc.protocol.rest.annotation.metadata.MetadataResolver; +import org.apache.dubbo.rpc.protocol.rest.exception.HttpClientException; +import org.apache.dubbo.rpc.protocol.rest.exception.RemoteServerInternalException; +import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager; +import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil; -import org.apache.http.HeaderElement; -import org.apache.http.HeaderElementIterator; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.config.SocketConfig; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.message.BasicHeaderElementIterator; -import org.apache.http.protocol.HTTP; -import org.jboss.resteasy.client.jaxrs.ResteasyClient; -import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; -import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget; -import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine; import org.jboss.resteasy.util.GetRestful; import javax.servlet.ServletContext; import javax.ws.rs.ProcessingException; import javax.ws.rs.WebApplicationException; import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; -import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; -import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; +import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_CLIENT; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_SERVER; -import static org.apache.dubbo.remoting.Constants.CONNECTIONS_KEY; -import static org.apache.dubbo.remoting.Constants.CONNECT_TIMEOUT_KEY; -import static org.apache.dubbo.remoting.Constants.DEFAULT_CONNECT_TIMEOUT; import static org.apache.dubbo.remoting.Constants.SERVER_KEY; -import static org.apache.dubbo.rpc.protocol.rest.Constants.EXTENSION_KEY; +import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; public class RestProtocol extends AbstractProxyProtocol { private static final int DEFAULT_PORT = 80; private static final String DEFAULT_SERVER = "jetty"; - - private static final int HTTPCLIENTCONNECTIONMANAGER_MAXPERROUTE = 20; - private static final int HTTPCLIENTCONNECTIONMANAGER_MAXTOTAL = 20; - private static final int HTTPCLIENT_KEEPALIVEDURATION = 30 * 1000; - private static final int HTTPCLIENTCONNECTIONMANAGER_CLOSEWAITTIME_MS = 1000; - private static final int HTTPCLIENTCONNECTIONMANAGER_CLOSEIDLETIME_S = 30; + private static final String DEFAULT_CLIENT = org.apache.dubbo.remoting.Constants.OK_HTTP; private final RestServerFactory serverFactory = new RestServerFactory(); - private final ConcurrentMap clients = new ConcurrentHashMap<>(); + private final ConcurrentMap> clients = new ConcurrentHashMap<>(); + + private final RestClientFactory clientFactory; - private volatile ConnectionMonitor connectionMonitor; + private final Set httpConnectionPreBuildIntercepts; - public RestProtocol() { + public RestProtocol(FrameworkModel frameworkModel) { super(WebApplicationException.class, ProcessingException.class); + this.clientFactory = frameworkModel.getExtensionLoader(RestClientFactory.class).getAdaptiveExtension(); + this.httpConnectionPreBuildIntercepts = frameworkModel.getExtensionLoader(HttpConnectionPreBuildIntercept.class).getSupportedExtensionInstances(); } - public void setHttpBinder(HttpBinder httpBinder) { - serverFactory.setHttpBinder(httpBinder); - } @Override public int getDefaultPort() { @@ -135,79 +136,100 @@ protected Runnable doExport(T impl, Class type, URL url) throws RpcExcept }; } + @Override - protected T doRefer(Class serviceType, URL url) throws RpcException { - ReferenceCountedClient referenceCountedClient = ConcurrentHashMapUtils.computeIfAbsent(clients, url.getAddress(), _key -> { - // TODO more configs to add - return createReferenceCountedClient(url); - }); + protected Invoker protocolBindingRefer(final Class type, final URL url) throws RpcException { - if (referenceCountedClient.isDestroyed()) { - referenceCountedClient = createReferenceCountedClient(url); - clients.put(url.getAddress(), referenceCountedClient); - } - referenceCountedClient.retain(); + ReferenceCountedClient refClient = + clients.computeIfAbsent(url.getAddress(), key -> createReferenceCountedClient(url)); - ResteasyClient resteasyClient = referenceCountedClient.getClient(); - for (String clazz : COMMA_SPLIT_PATTERN.split(url.getParameter(EXTENSION_KEY, ""))) { - if (!StringUtils.isEmpty(clazz)) { + refClient.retain(); + + // resolve metadata + Map> metadataMap = MetadataResolver.resolveConsumerServiceMetadata(type, url); + + Invoker invoker = new AbstractInvoker(type, url, new String[]{INTERFACE_KEY, GROUP_KEY, TOKEN_KEY}) { + @Override + protected Result doInvoke(Invocation invocation) { try { - resteasyClient.register(Thread.currentThread().getContextClassLoader().loadClass(clazz.trim())); - } catch (ClassNotFoundException e) { - throw new RpcException("Error loading JAX-RS extension class: " + clazz.trim(), e); + RestMethodMetadata restMethodMetadata = metadataMap.get(invocation.getMethodName()).get(ParameterTypesComparator.getInstance(invocation.getParameterTypes())); + + RequestTemplate requestTemplate = new RequestTemplate(invocation, restMethodMetadata.getRequest().getMethod(), url.getAddress(), getContextPath(url)); + + HttpConnectionCreateContext httpConnectionCreateContext = new HttpConnectionCreateContext(); + // TODO dynamic load config + httpConnectionCreateContext.setConnectionConfig(new HttpConnectionConfig()); + httpConnectionCreateContext.setRequestTemplate(requestTemplate); + httpConnectionCreateContext.setRestMethodMetadata(restMethodMetadata); + httpConnectionCreateContext.setInvocation(invocation); + httpConnectionCreateContext.setUrl(url); + + for (HttpConnectionPreBuildIntercept intercept : httpConnectionPreBuildIntercepts) { + intercept.intercept(httpConnectionCreateContext); + } + + CompletableFuture future = refClient.getClient().send(requestTemplate); + CompletableFuture responseFuture = new CompletableFuture<>(); + AsyncRpcResult asyncRpcResult = new AsyncRpcResult(responseFuture, invocation); + future.whenComplete((r, t) -> { + if (t != null) { + responseFuture.completeExceptionally(t); + } else { + AppResponse appResponse = new AppResponse(); + try { + int responseCode = r.getResponseCode(); + MediaType mediaType = MediaType.TEXT_PLAIN; + + if (400 < responseCode && responseCode < 500) { + throw new HttpClientException(r.getMessage()); + } else if (responseCode >= 500) { + throw new RemoteServerInternalException(r.getMessage()); + } else if (responseCode < 400) { + mediaType = MediaTypeUtil.convertMediaType(r.getContentType()); + } + + + Object value = HttpMessageCodecManager.httpMessageDecode(r.getBody(), + restMethodMetadata.getReflectMethod().getReturnType(), mediaType); + appResponse.setValue(value); + Map headers = r.headers() + .entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0))); + appResponse.setAttachments(headers); + responseFuture.complete(appResponse); + } catch (Exception e) { + responseFuture.completeExceptionally(e); + } + } + }); + return asyncRpcResult; + } catch (RpcException e) { + if (e.getCode() == RpcException.UNKNOWN_EXCEPTION) { + e.setCode(getErrorCode(e.getCause())); + } + throw e; } } - } - // TODO protocol - ResteasyWebTarget target = resteasyClient.target("http://" + url.getAddress() + "/" + getContextPath(url)); - return target.proxy(serviceType); + @Override + public void destroy() { + super.destroy(); + invokers.remove(this); + destroyInternal(url); + } + }; + invokers.add(invoker); + return invoker; } - private ReferenceCountedClient createReferenceCountedClient(URL url) { - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); - // 20 is the default maxTotal of current PoolingClientConnectionManager - connectionManager.setMaxTotal(url.getParameter(CONNECTIONS_KEY, HTTPCLIENTCONNECTIONMANAGER_MAXTOTAL)); - connectionManager.setDefaultMaxPerRoute(url.getParameter(CONNECTIONS_KEY, HTTPCLIENTCONNECTIONMANAGER_MAXPERROUTE)); - if (connectionMonitor == null) { - connectionMonitor = new ConnectionMonitor(); - connectionMonitor.start(); - } - connectionMonitor.addConnectionManager(url.getAddress(), connectionManager); - - RequestConfig requestConfig = RequestConfig.custom() - .setConnectTimeout(url.getParameter(CONNECT_TIMEOUT_KEY, DEFAULT_CONNECT_TIMEOUT)) - .setSocketTimeout(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT)) - .build(); - - SocketConfig socketConfig = SocketConfig.custom() - .setSoKeepAlive(true) - .setTcpNoDelay(true) - .build(); - - CloseableHttpClient httpClient = HttpClientBuilder.create() - .setConnectionManager(connectionManager) - .setKeepAliveStrategy((response, context) -> { - HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE)); - while (it.hasNext()) { - HeaderElement he = it.nextElement(); - String param = he.getName(); - String value = he.getValue(); - if (value != null && param.equalsIgnoreCase(TIMEOUT_KEY)) { - return Long.parseLong(value) * 1000; - } - } - return HTTPCLIENT_KEEPALIVEDURATION; - }) - .setDefaultRequestConfig(requestConfig) - .setDefaultSocketConfig(socketConfig) - .build(); - ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient/*, localContext*/); + private ReferenceCountedClient createReferenceCountedClient(URL url) throws RpcException { - ResteasyClient resteasyClient = new ResteasyClientBuilder().httpEngine(engine).build(); - resteasyClient.register(RpcContextFilter.class); - return new ReferenceCountedClient(resteasyClient); + // url -> RestClient + RestClient restClient = clientFactory.createRestClient(url); + + return new ReferenceCountedClient<>(restClient); } @Override @@ -223,10 +245,6 @@ public void destroy() { } super.destroy(); - if (connectionMonitor != null) { - connectionMonitor.shutdown(); - } - for (Map.Entry entry : serverMap.entrySet()) { try { if (logger.isInfoEnabled()) { @@ -281,55 +299,9 @@ protected void destroyInternal(URL url) { ReferenceCountedClient referenceCountedClient = clients.get(url.getAddress()); if (referenceCountedClient != null && referenceCountedClient.release()) { clients.remove(url.getAddress()); - connectionMonitor.destroyManager(url); } } catch (Exception e) { logger.warn(PROTOCOL_ERROR_CLOSE_CLIENT, "", "", "Failed to close unused resources in rest protocol. interfaceName [" + url.getServiceInterface() + "]", e); } } - - protected class ConnectionMonitor extends Thread { - private volatile boolean shutdown; - /** - * The lifecycle of {@code PoolingHttpClientConnectionManager} instance is bond with ReferenceCountedClient - */ - private final Map connectionManagers = new ConcurrentHashMap<>(); - - public void addConnectionManager(String address, PoolingHttpClientConnectionManager connectionManager) { - connectionManagers.putIfAbsent(address, connectionManager); - } - - @Override - public void run() { - try { - while (!shutdown) { - synchronized (this) { - wait(HTTPCLIENTCONNECTIONMANAGER_CLOSEWAITTIME_MS); - for (PoolingHttpClientConnectionManager connectionManager : connectionManagers.values()) { - connectionManager.closeExpiredConnections(); - connectionManager.closeIdleConnections(HTTPCLIENTCONNECTIONMANAGER_CLOSEIDLETIME_S, TimeUnit.SECONDS); - } - } - } - } catch (InterruptedException ex) { - shutdown(); - } - } - - public void shutdown() { - shutdown = true; - connectionManagers.clear(); - synchronized (this) { - notifyAll(); - } - } - - // destroy the connection manager of a specific address when ReferenceCountedClient is destroyed. - private void destroyManager(URL url) { - PoolingHttpClientConnectionManager connectionManager = connectionManagers.remove(url.getAddress()); - if (connectionManager != null) { - connectionManager.close(); - } - } - } } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestServerFactory.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestServerFactory.java index 6fcf7f6f088..265987241e5 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestServerFactory.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestServerFactory.java @@ -17,19 +17,15 @@ package org.apache.dubbo.rpc.protocol.rest; import org.apache.dubbo.remoting.http.HttpBinder; +import org.apache.dubbo.rpc.model.FrameworkModel; /** * Only the server that implements servlet container * could support something like @Context injection of servlet objects. - * */ public class RestServerFactory { - private HttpBinder httpBinder; - - public void setHttpBinder(HttpBinder httpBinder) { - this.httpBinder = httpBinder; - } + private static final HttpBinder httpBinder = FrameworkModel.defaultModel().getAdaptiveExtension(HttpBinder.class); public RestProtocolServer createServer(String name) { // TODO move names to Constants diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/BaseParseContext.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/BaseParseContext.java new file mode 100644 index 00000000000..e2fdc252efd --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/BaseParseContext.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.annotation; + + +import org.apache.dubbo.metadata.rest.ArgInfo; + +import java.util.List; + +public class BaseParseContext { + + + protected List args; + + protected List argInfos; + + + public List getArgs() { + return args; + } + + public void setArgs(List args) { + this.args = args; + } + + public List getArgInfos() { + return argInfos; + } + + public void setArgInfos(List argInfos) { + this.argInfos = argInfos; + } + + public ArgInfo getArgInfoByIndex(int index) { + return getArgInfos().get(index); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParser.java new file mode 100644 index 00000000000..e8af563d3ef --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParser.java @@ -0,0 +1,24 @@ +/* + * 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.rest.annotation; + +import org.apache.dubbo.metadata.rest.ArgInfo; + + +public interface ParamParser { + void parse(T parseContext, ArgInfo argInfo); +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParserManager.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParserManager.java new file mode 100644 index 00000000000..476f7aaed0f --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParserManager.java @@ -0,0 +1,61 @@ +/* + * 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.rest.annotation; + + +import org.apache.dubbo.metadata.rest.ArgInfo; +import org.apache.dubbo.rpc.model.FrameworkModel; +import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BaseConsumerParamParser; +import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.ConsumerParseContext; + +import java.util.List; +import java.util.Set; + +public class ParamParserManager { + + + private static final Set consumerParamParsers = + FrameworkModel.defaultModel().getExtensionLoader(BaseConsumerParamParser.class).getSupportedExtensionInstances(); + + /** + * provider Design Description: + *

+ * Object[] args=new Object[0]; + * List argsList=new ArrayList<>; + *

+ * setValueByIndex(int index,Object value); + *

+ * args=toArray(new Object[0]); + */ + public void consumerParamParse(ConsumerParseContext parseContext) { + + List args = parseContext.getArgs(); + + for (int i = 0; i < args.size(); i++) { + for (BaseConsumerParamParser paramParser : consumerParamParsers) { + ArgInfo argInfoByIndex = parseContext.getArgInfoByIndex(i); + + if (!paramParser.paramTypeMatch(argInfoByIndex)) { + continue; + } + + paramParser.parse(parseContext, argInfoByIndex); + } + } + + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionConfig.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionConfig.java new file mode 100644 index 00000000000..e9a9931c46a --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionConfig.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.annotation.consumer; + + +import org.apache.dubbo.common.serialize.Constants; + +public class HttpConnectionConfig { + + + private int connectTimeout; + private int readTimeout; + private int chunkLength = 8196; + private byte serialization = Constants.FASTJSON2_SERIALIZATION_ID; + private int keepAlive = 60; + + + public int getConnectTimeout() { + return connectTimeout; + } + + public int getReadTimeout() { + return readTimeout; + } + + public int getChunkLength() { + return chunkLength; + } + + public byte getSerialization() { + return serialization; + } + + public int getKeepAlive() { + return keepAlive; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionCreateContext.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionCreateContext.java new file mode 100644 index 00000000000..73e0635cc40 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionCreateContext.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.rpc.protocol.rest.annotation.consumer; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.metadata.rest.ServiceRestMetadata; +import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.rpc.Invocation; + +public class HttpConnectionCreateContext { + + private RequestTemplate requestTemplate; + private HttpConnectionConfig connectionConfig; + private RestMethodMetadata restMethodMetadata; + private Invocation invocation; + private URL url; + + public HttpConnectionCreateContext() { + } + + + public void setRequestTemplate(RequestTemplate requestTemplate) { + this.requestTemplate = requestTemplate; + } + + public void setConnectionConfig(HttpConnectionConfig connectionConfig) { + this.connectionConfig = connectionConfig; + } + + + public RequestTemplate getRequestTemplate() { + return requestTemplate; + } + + public HttpConnectionConfig getConnectionConfig() { + return connectionConfig; + } + + public ServiceRestMetadata getServiceRestMetadata() { + return restMethodMetadata.getServiceRestMetadata(); + } + + public RestMethodMetadata getRestMethodMetadata() { + return restMethodMetadata; + } + + public void setRestMethodMetadata(RestMethodMetadata restMethodMetadata) { + this.restMethodMetadata = restMethodMetadata; + } + + public Invocation getInvocation() { + return invocation; + } + + public void setInvocation(Invocation invocation) { + this.invocation = invocation; + } + + public URL getUrl() { + return url; + } + + public void setUrl(URL url) { + this.url = url; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionPreBuildIntercept.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionPreBuildIntercept.java new file mode 100644 index 00000000000..3862e183147 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionPreBuildIntercept.java @@ -0,0 +1,25 @@ +/* + * 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.rest.annotation.consumer; + +import org.apache.dubbo.common.extension.ExtensionScope; +import org.apache.dubbo.common.extension.SPI; + +@SPI(scope = ExtensionScope.FRAMEWORK) +public interface HttpConnectionPreBuildIntercept { + void intercept(HttpConnectionCreateContext connectionCreateContext); +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AddMustAttachmentIntercept.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AddMustAttachmentIntercept.java new file mode 100644 index 00000000000..446cd4e6f34 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AddMustAttachmentIntercept.java @@ -0,0 +1,48 @@ +/* + * 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.rest.annotation.consumer.inercept; + + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.metadata.rest.ServiceRestMetadata; +import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionConfig; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept; +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; + +@Activate(value = RestConstant.ADD_MUST_ATTTACHMENT,order = 1) +public class AddMustAttachmentIntercept implements HttpConnectionPreBuildIntercept { + + @Override + public void intercept(HttpConnectionCreateContext connectionCreateContext) { + + RequestTemplate requestTemplate = connectionCreateContext.getRequestTemplate(); + ServiceRestMetadata serviceRestMetadata = connectionCreateContext.getServiceRestMetadata(); + HttpConnectionConfig connectionConfig = connectionCreateContext.getConnectionConfig(); + + + requestTemplate.addHeader(RestConstant.GROUP, serviceRestMetadata.getGroup()); + requestTemplate.addHeader(RestConstant.VERSION, serviceRestMetadata.getVersion()); + requestTemplate.addHeader(RestConstant.PATH, serviceRestMetadata.getServiceInterface()); + requestTemplate.addKeepAliveHeader(connectionConfig.getKeepAlive()); + + + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AttachmentIntercept.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AttachmentIntercept.java new file mode 100644 index 00000000000..3903aa654c5 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AttachmentIntercept.java @@ -0,0 +1,70 @@ +/* + * 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.rest.annotation.consumer.inercept; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept; +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; + +import java.nio.charset.StandardCharsets; +import java.util.Map; +@Activate(value = RestConstant.RPCCONTEXT_INTERCEPT,order = 3) +public class AttachmentIntercept implements HttpConnectionPreBuildIntercept { + + @Override + public void intercept(HttpConnectionCreateContext connectionCreateContext) { + + RequestTemplate requestTemplate = connectionCreateContext.getRequestTemplate(); + int size = 0; + for (Map.Entry entry : connectionCreateContext.getInvocation().getObjectAttachments().entrySet()) { + String key = entry.getKey(); + String value = String.valueOf(entry.getValue()); + if (illegalHttpHeaderKey(key) || illegalHttpHeaderValue(value)) { + throw new IllegalArgumentException("The attachments of " + RpcContext.class.getSimpleName() + " must not contain ',' or '=' when using rest protocol"); + } + + // TODO for now we don't consider the differences of encoding and server limit + if (value != null) { + size += value.getBytes(StandardCharsets.UTF_8).length; + } + if (size > RestConstant.MAX_HEADER_SIZE) { + throw new IllegalArgumentException("The attachments of " + RpcContext.class.getSimpleName() + " is too big"); + } + + String attachments = key + "=" + value; + requestTemplate.addHeader(RestConstant.DUBBO_ATTACHMENT_HEADER, attachments); + } + } + + private boolean illegalHttpHeaderKey(String key) { + if (StringUtils.isNotEmpty(key)) { + return key.contains(",") || key.contains("="); + } + return false; + } + + private boolean illegalHttpHeaderValue(String value) { + if (StringUtils.isNotEmpty(value)) { + return value.contains(","); + } + return false; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/ParamParseIntercept.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/ParamParseIntercept.java new file mode 100644 index 00000000000..9babb8c6055 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/ParamParseIntercept.java @@ -0,0 +1,39 @@ +/* + * 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.rest.annotation.consumer.inercept; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.rpc.protocol.rest.annotation.ParamParserManager; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept; +import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.ConsumerParseContext; + +import java.util.Arrays; + +@Activate(value = "paramparse",order = 5) +public class ParamParseIntercept implements HttpConnectionPreBuildIntercept { + private static final ParamParserManager paramParser = new ParamParserManager(); + + @Override + public void intercept(HttpConnectionCreateContext connectionCreateContext) { + + ConsumerParseContext consumerParseContext = new ConsumerParseContext(connectionCreateContext.getRequestTemplate()); + consumerParseContext.setArgInfos(connectionCreateContext.getRestMethodMetadata().getArgInfos()); + consumerParseContext.setArgs(Arrays.asList(connectionCreateContext.getInvocation().getArguments())); + paramParser.consumerParamParse(consumerParseContext); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/PathVariableIntercept.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/PathVariableIntercept.java new file mode 100644 index 00000000000..8329b086ade --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/PathVariableIntercept.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept; + + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.metadata.rest.ArgInfo; +import org.apache.dubbo.metadata.rest.PathUtil; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept; +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; + +import java.util.Arrays; +import java.util.List; + +@Activate(value = RestConstant.PATH_INTERCEPT,order = 4) +public class PathVariableIntercept implements HttpConnectionPreBuildIntercept { + + @Override + public void intercept(HttpConnectionCreateContext connectionCreateContext) { + + RestMethodMetadata restMethodMetadata = connectionCreateContext.getRestMethodMetadata(); + RequestTemplate requestTemplate = connectionCreateContext.getRequestTemplate(); + + List argInfos = restMethodMetadata.getArgInfos(); + + // path variable parse + String path = PathUtil.resolvePathVariable(restMethodMetadata.getRequest().getPath(), argInfos, Arrays.asList(connectionCreateContext.getInvocation().getArguments())); + requestTemplate.path(path); + + + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/RequestHeaderIntercept.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/RequestHeaderIntercept.java new file mode 100644 index 00000000000..e9ee6871184 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/RequestHeaderIntercept.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept; + + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept; +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; + +import java.util.Collection; +import java.util.Set; + +@Activate(value = RestConstant.REQUEST_HEADER_INTERCEPT,order = 2) +public class RequestHeaderIntercept implements HttpConnectionPreBuildIntercept { + + @Override + public void intercept(HttpConnectionCreateContext connectionCreateContext) { + + RestMethodMetadata restMethodMetadata = connectionCreateContext.getRestMethodMetadata(); + RequestTemplate requestTemplate = connectionCreateContext.getRequestTemplate(); + + + + Set consumes = restMethodMetadata.getRequest().getConsumes(); + + requestTemplate.addHeaders(RestConstant.CONTENT_TYPE, consumes); + + Collection headers = requestTemplate.getHeaders(RestConstant.ACCEPT); + if (headers == null || headers.isEmpty()) { + requestTemplate.addHeader(RestConstant.ACCEPT, RestConstant.DEFAULT_ACCEPT); + } + + + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/SerializeBodyIntercept.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/SerializeBodyIntercept.java new file mode 100644 index 00000000000..6902cb987b5 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/SerializeBodyIntercept.java @@ -0,0 +1,66 @@ +/* + * 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.rest.annotation.consumer.inercept; + + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.LoggerCodeConstants; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext; +import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept; +import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant; +import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager; +import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil; + +import java.io.ByteArrayOutputStream; +import java.util.Collection; + +@Activate(value = RestConstant.SERIALIZE_INTERCEPT, order = Integer.MAX_VALUE) +public class SerializeBodyIntercept implements HttpConnectionPreBuildIntercept { + + private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SerializeBodyIntercept.class); + + @Override + public void intercept(HttpConnectionCreateContext connectionCreateContext) { + RequestTemplate requestTemplate = connectionCreateContext.getRequestTemplate(); + + if (requestTemplate.isBodyEmpty()) { + return; + } + + + try { + Object unSerializedBody = requestTemplate.getUnSerializedBody(); + URL url = connectionCreateContext.getUrl(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + Collection headers = requestTemplate.getHeaders(RestConstant.CONTENT_TYPE); + MediaType mediaType = MediaTypeUtil.convertMediaType(headers.toArray(new String[0])); + HttpMessageCodecManager.httpMessageEncode(outputStream, unSerializedBody, url, mediaType); + requestTemplate.serializeBody(outputStream.toByteArray()); + } catch (Exception e) { + logger.error(LoggerCodeConstants.PROTOCOL_ERROR_DESERIALIZE, "", "", "Rest SerializeBodyIntercept serialize error: {}", e); + } + + + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/metadata/MetadataResolver.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/metadata/MetadataResolver.java new file mode 100644 index 00000000000..69715715a1f --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/metadata/MetadataResolver.java @@ -0,0 +1,57 @@ +/* + * 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.rest.annotation.metadata; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.metadata.ParameterTypesComparator; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.metadata.rest.ServiceRestMetadata; +import org.apache.dubbo.metadata.rest.ServiceRestMetadataResolver; +import org.apache.dubbo.rpc.protocol.rest.exception.CodeStyleNotSupportException; + +import java.util.Map; + +public class MetadataResolver { + private MetadataResolver() { + } + + /** + * for consumer + * + * @param targetClass target service class + * @param url consumer url + * @return rest metadata + * @throws CodeStyleNotSupportException not support type + */ + public static Map> resolveConsumerServiceMetadata(Class targetClass, URL url) { + ExtensionLoader extensionLoader = url.getOrDefaultApplicationModel().getExtensionLoader(ServiceRestMetadataResolver.class); + + for (ServiceRestMetadataResolver serviceRestMetadataResolver : extensionLoader.getSupportedExtensionInstances()) { + if (serviceRestMetadataResolver.supports(targetClass, true)) { + ServiceRestMetadata serviceRestMetadata = new ServiceRestMetadata(url.getServiceInterface(), url.getVersion(), url.getGroup(), true); + ServiceRestMetadata resolve = serviceRestMetadataResolver.resolve(targetClass, serviceRestMetadata); + return resolve.getMethodToServiceMap(); + } + } + + // TODO support Dubbo style service + throw new CodeStyleNotSupportException("service is: " + targetClass + ", only support " + extensionLoader.getSupportedExtensions() + " annotation"); + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BaseConsumerParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BaseConsumerParamParser.java new file mode 100644 index 00000000000..74c2ca15212 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BaseConsumerParamParser.java @@ -0,0 +1,29 @@ +/* + * 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.rest.annotation.param.parse.consumer; + + +import org.apache.dubbo.common.extension.ExtensionScope; +import org.apache.dubbo.common.extension.SPI; +import org.apache.dubbo.metadata.rest.ArgInfo; +import org.apache.dubbo.rpc.protocol.rest.annotation.ParamParser; + +@SPI(scope = ExtensionScope.FRAMEWORK) +public interface BaseConsumerParamParser extends ParamParser { + + boolean paramTypeMatch(ArgInfo argInfo); +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BodyConsumerParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BodyConsumerParamParser.java new file mode 100644 index 00000000000..0e9e02a9af0 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BodyConsumerParamParser.java @@ -0,0 +1,45 @@ +/* + * 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.rest.annotation.param.parse.consumer; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.metadata.rest.ArgInfo; +import org.apache.dubbo.metadata.rest.ParamType; +import org.apache.dubbo.remoting.http.RequestTemplate; + +import java.util.List; + +@Activate("consumer-body") +public class BodyConsumerParamParser implements BaseConsumerParamParser { + @Override + public void parse(ConsumerParseContext parseContext, ArgInfo argInfo) { + + + List args = parseContext.getArgs(); + + RequestTemplate requestTemplate = parseContext.getRequestTemplate(); + + requestTemplate.body(args.get(argInfo.getIndex())); + + + } + + @Override + public boolean paramTypeMatch(ArgInfo argInfo) { + return ParamType.BODY.supportAnno(argInfo.getParamAnnotationType()); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ConsumerParseContext.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ConsumerParseContext.java new file mode 100644 index 00000000000..672b30e4de4 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ConsumerParseContext.java @@ -0,0 +1,34 @@ +/* + * 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.rest.annotation.param.parse.consumer; + + +import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.rpc.protocol.rest.annotation.BaseParseContext; + +public class ConsumerParseContext extends BaseParseContext { + private RequestTemplate requestTemplate; + + public ConsumerParseContext(RequestTemplate requestTemplate) { + this.requestTemplate = requestTemplate; + } + + public RequestTemplate getRequestTemplate() { + return requestTemplate; + } + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/FormConsumerParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/FormConsumerParamParser.java new file mode 100644 index 00000000000..dba932b8def --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/FormConsumerParamParser.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.metadata.rest.ArgInfo; +import org.apache.dubbo.metadata.rest.ParamType; +import org.apache.dubbo.remoting.http.RequestTemplate; +import org.apache.dubbo.rpc.protocol.rest.util.MultiValueCreator; + +import java.util.List; + +@Activate("consumer-form") +public class FormConsumerParamParser implements BaseConsumerParamParser { + @Override + public void parse(ConsumerParseContext parseContext, ArgInfo argInfo) { + + + List args = parseContext.getArgs(); + + RequestTemplate requestTemplate = parseContext.getRequestTemplate(); + Object value = args.get(argInfo.getIndex()); + + Object unSerializedBody = requestTemplate.getUnSerializedBody(); + if (unSerializedBody == null) { + unSerializedBody = MultiValueCreator.createMultiValueMap(); + } + + MultiValueCreator.add(unSerializedBody, argInfo.getAnnotationNameAttribute(), String.valueOf(value)); + + requestTemplate.body(unSerializedBody); + + + } + + @Override + public boolean paramTypeMatch(ArgInfo argInfo) { + return ParamType.FORM.supportAnno(argInfo.getParamAnnotationType()); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/HeaderConsumerParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/HeaderConsumerParamParser.java new file mode 100644 index 00000000000..b53591e4918 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/HeaderConsumerParamParser.java @@ -0,0 +1,41 @@ +/* + * 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.rest.annotation.param.parse.consumer; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.metadata.rest.ArgInfo; +import org.apache.dubbo.metadata.rest.ParamType; +import org.apache.dubbo.remoting.http.RequestTemplate; + +import java.util.List; + +@Activate("consumer-header") +public class HeaderConsumerParamParser implements BaseConsumerParamParser { + @Override + public void parse(ConsumerParseContext parseContext, ArgInfo argInfo) { + List args = parseContext.getArgs(); + + RequestTemplate requestTemplate = parseContext.getRequestTemplate(); + + requestTemplate.addHeader(argInfo.getParamName(), args.get(argInfo.getIndex())); + } + + @Override + public boolean paramTypeMatch(ArgInfo argInfo) { + return ParamType.HEADER.supportAnno(argInfo.getParamAnnotationType()); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ParameterConsumerParamParser.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ParameterConsumerParamParser.java new file mode 100644 index 00000000000..eb2746f1bb3 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ParameterConsumerParamParser.java @@ -0,0 +1,41 @@ +/* + * 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.rest.annotation.param.parse.consumer; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.metadata.rest.ArgInfo; +import org.apache.dubbo.metadata.rest.ParamType; +import org.apache.dubbo.remoting.http.RequestTemplate; + +import java.util.List; + +@Activate("consumer-parameter") +public class ParameterConsumerParamParser implements BaseConsumerParamParser{ + @Override + public void parse(ConsumerParseContext parseContext, ArgInfo argInfo) { + List args = parseContext.getArgs(); + + RequestTemplate requestTemplate = parseContext.getRequestTemplate(); + + requestTemplate.addParam(argInfo.getParamName(), args.get(argInfo.getIndex())); + } + + @Override + public boolean paramTypeMatch(ArgInfo argInfo) { + return ParamType.PARAM.supportAnno(argInfo.getParamAnnotationType()); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/constans/RestConstant.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/constans/RestConstant.java new file mode 100644 index 00000000000..8fe65f03a19 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/constans/RestConstant.java @@ -0,0 +1,60 @@ +/* + * 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.rest.constans; + +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.remoting.Constants; + +public interface RestConstant { + String INTERFACE = CommonConstants.INTERFACE_KEY; + String METHOD = CommonConstants.METHOD_KEY; + String PARAMETER_TYPES_DESC = CommonConstants.GENERIC_PARAMETER_DESC; + String VERSION = CommonConstants.VERSION_KEY; + String GROUP = CommonConstants.GROUP_KEY; + String PATH = CommonConstants.PATH_KEY; + String HOST = CommonConstants.HOST_KEY; + String LOCAL_ADDR = "LOCAL_ADDR"; + String REMOTE_ADDR = "REMOTE_ADDR"; + String LOCAL_PORT = "LOCAL_PORT"; + String REMOTE_PORT = "REMOTE_PORT"; + String SERIALIZATION_KEY = Constants.SERIALIZATION_KEY; + String PROVIDER_BODY_PARSE = "body"; + String PROVIDER_PARAM_PARSE = "param"; + String PROVIDER_HEADER_PARSE = "header"; + String PROVIDER_PATH_PARSE = "path"; + String PROVIDER_REQUEST_PARSE = "reuqest"; + String DUBBO_ATTACHMENT_HEADER = "Dubbo-Attachments"; + int MAX_HEADER_SIZE = 8 * 1024; + + String ADD_MUST_ATTTACHMENT = "must-intercept"; + String RPCCONTEXT_INTERCEPT = "rpc-context"; + String SERIALIZE_INTERCEPT = "serialize"; + String CONTENT_TYPE_INTERCEPT = "content-type"; + String PATH_SEPARATOR = "/"; + String REQUEST_PARAM_INTERCEPT = "param"; + String REQUEST_HEADER_INTERCEPT = "header"; + String PATH_INTERCEPT = "path"; + String KEEP_ALIVE_HEADER = "Keep-Alive"; + String CONNECTION = "Connection"; + String KEEP_ALIVE = "keep-alive"; + String CONTENT_TYPE = "Content-Type"; + String APPLICATION_JSON_VALUE = "application/json"; + String APPLICATION_FORM_URLENCODED_VALUE = "application/x-www-form-urlencoded"; + String TEXT_PLAIN = "text/plain"; + String ACCEPT = "Accept"; + String DEFAULT_ACCEPT = "*/*"; +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/CodeStyleNotSupportException.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/CodeStyleNotSupportException.java new file mode 100644 index 00000000000..db68cf1ac79 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/CodeStyleNotSupportException.java @@ -0,0 +1,24 @@ +/* + * 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.rest.exception; + +public class CodeStyleNotSupportException extends RuntimeException{ + + public CodeStyleNotSupportException(String message) { + super(message); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/HttpClientException.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/HttpClientException.java new file mode 100644 index 00000000000..709e12af1fb --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/HttpClientException.java @@ -0,0 +1,24 @@ +/* + * 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.rest.exception; + +public class HttpClientException extends RuntimeException { + + public HttpClientException(String message) { + super("dubbo http rest protocol param error :"+message); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RemoteServerInternalException.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RemoteServerInternalException.java new file mode 100644 index 00000000000..1d1219fdbff --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RemoteServerInternalException.java @@ -0,0 +1,24 @@ +/* + * 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.rest.exception; + +public class RemoteServerInternalException extends RuntimeException { + + public RemoteServerInternalException(String message) { + super("dubbo http rest protocol remote error :"+message); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/UnSupportContentTypeException.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/UnSupportContentTypeException.java new file mode 100644 index 00000000000..f895f6ec44b --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/UnSupportContentTypeException.java @@ -0,0 +1,27 @@ +/* + * 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.rest.exception; + +import org.apache.dubbo.metadata.rest.media.MediaType; + +public class UnSupportContentTypeException extends RuntimeException { + + public UnSupportContentTypeException(String message) { + + super("Current Support content type: " + MediaType.getAllContentType() + "; Do not support content type" + message); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodec.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodec.java new file mode 100644 index 00000000000..1b6666b8f28 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodec.java @@ -0,0 +1,30 @@ +/* + * 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.rest.message; + + +import org.apache.dubbo.common.extension.ExtensionScope; +import org.apache.dubbo.common.extension.SPI; +import org.apache.dubbo.metadata.rest.media.MediaType; + +@SPI(scope = ExtensionScope.FRAMEWORK) +public interface HttpMessageCodec extends HttpMessageDecode, HttpMessageEncode { + + + boolean contentTypeSupport(MediaType mediaType, Class targetType); + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodecManager.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodecManager.java new file mode 100644 index 00000000000..019927ca360 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodecManager.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.message; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.rpc.model.FrameworkModel; +import org.apache.dubbo.rpc.protocol.rest.exception.UnSupportContentTypeException; + +import java.io.OutputStream; +import java.util.Set; + +public class HttpMessageCodecManager { + private static final Set httpMessageCodecs = + FrameworkModel.defaultModel().getExtensionLoader(HttpMessageCodec.class).getSupportedExtensionInstances(); + + + public static Object httpMessageDecode(byte[] body, Class type, MediaType mediaType) throws Exception { + for (HttpMessageCodec httpMessageCodec : httpMessageCodecs) { + if (httpMessageCodec.contentTypeSupport(mediaType, type)) { + return httpMessageCodec.decode(body, type); + } + } + throw new UnSupportContentTypeException("UnSupport content-type :" + mediaType.value); + } + + public static void httpMessageEncode(OutputStream outputStream, Object unSerializedBody, URL url, MediaType mediaType) throws Exception { + for (HttpMessageCodec httpMessageCodec : httpMessageCodecs) { + if (httpMessageCodec.contentTypeSupport(mediaType, unSerializedBody.getClass())) { + httpMessageCodec.encode(outputStream, unSerializedBody, url); + return; + } + } + throw new UnSupportContentTypeException("UnSupport content-type :" + mediaType.value); + } + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageDecode.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageDecode.java new file mode 100644 index 00000000000..12ae3816859 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageDecode.java @@ -0,0 +1,24 @@ +/* + * 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.rest.message; + + +public interface HttpMessageDecode{ + + Object decode(InputStream body, Class targetType) throws Exception; + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageEncode.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageEncode.java new file mode 100644 index 00000000000..6d35d9c43e4 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageEncode.java @@ -0,0 +1,28 @@ +/* + * 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.rest.message; + + +import org.apache.dubbo.common.URL; + + + +public interface HttpMessageEncode { + + void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception; + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/MediaTypeMatcher.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/MediaTypeMatcher.java new file mode 100644 index 00000000000..2863f258936 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/MediaTypeMatcher.java @@ -0,0 +1,64 @@ +/* + * 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.rest.message; + +import org.apache.dubbo.metadata.rest.media.MediaType; + +import java.util.ArrayList; +import java.util.List; + +public enum MediaTypeMatcher { + + + MULTI_VALUE(createMediaList(MediaType.APPLICATION_FORM_URLENCODED_VALUE)), + APPLICATION_JSON(createMediaList(MediaType.APPLICATION_JSON_VALUE)), + TEXT_PLAIN(createMediaList(MediaType.TEXT_PLAIN, MediaType.OCTET_STREAM)), + TEXT_XML(createMediaList(MediaType.TEXT_XML)), + + ; + + private List mediaTypes; + + + MediaTypeMatcher(List mediaTypes) { + this.mediaTypes = mediaTypes; + } + + + private static List createMediaList(MediaType... mediaTypes) { + List mediaTypeList = getDefaultList(); + + for (MediaType mediaType : mediaTypes) { + + mediaTypeList.add(mediaType); + } + return mediaTypeList; + } + + private static List getDefaultList() { + + List defaultList = new ArrayList<>(); + defaultList.add(MediaType.ALL_VALUE); + return defaultList; + } + + public boolean mediaSupport(MediaType mediaType) { + return mediaTypes.contains(mediaType); + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/ByteArrayCodec.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/ByteArrayCodec.java new file mode 100644 index 00000000000..4210e235694 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/ByteArrayCodec.java @@ -0,0 +1,45 @@ +/* + * 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.rest.message.codec; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec; + +import java.io.OutputStream; + +@Activate("byteArray") +public class ByteArrayCodec implements HttpMessageCodec { + + + @Override + public Object decode(byte[] body, Class targetType) throws Exception { + return body; + } + + @Override + public boolean contentTypeSupport(MediaType mediaType, Class targetType) { + return byte[].class.equals(targetType); + } + + + @Override + public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception { + outputStream.write((byte[]) unSerializedBody); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/JsonCodec.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/JsonCodec.java new file mode 100644 index 00000000000..095c33bddac --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/JsonCodec.java @@ -0,0 +1,57 @@ +/* + * 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.rest.message.codec; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.utils.JsonUtils; +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec; +import org.apache.dubbo.rpc.protocol.rest.message.MediaTypeMatcher; +import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils; + +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.Set; + +@Activate("json") +public class JsonCodec implements HttpMessageCodec { + private static final Set unSupportClasses = new HashSet<>(); + + static { + + unSupportClasses.add(byte[].class); + unSupportClasses.add(String.class); + } + + @Override + public Object decode(byte[] body, Class targetType) throws Exception { + return DataParseUtils.jsonConvert(targetType, body); + } + + @Override + public boolean contentTypeSupport(MediaType mediaType, Class targetType) { + return MediaTypeMatcher.APPLICATION_JSON.mediaSupport(mediaType) && !unSupportClasses.contains(targetType); + } + + + @Override + public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception { + outputStream.write(JsonUtils.getJson().toJson(unSerializedBody).getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/MultiValueCodec.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/MultiValueCodec.java new file mode 100644 index 00000000000..97fbb5bedb4 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/MultiValueCodec.java @@ -0,0 +1,48 @@ +/* + * 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.rest.message.codec; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec; +import org.apache.dubbo.rpc.protocol.rest.message.MediaTypeMatcher; +import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils; + +import java.io.OutputStream; +import java.util.Map; + +@Activate("multiValue") +public class MultiValueCodec implements HttpMessageCodec { + + + @Override + public Object decode(byte[] body, Class targetType) throws Exception { + // TODO java bean get set convert + return DataParseUtils.multipartFormConvert(body); + } + + @Override + public boolean contentTypeSupport(MediaType mediaType,Class targetType) { + return MediaTypeMatcher.MULTI_VALUE.mediaSupport(mediaType); + } + + @Override + public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception { + DataParseUtils.writeFormContent((Map) unSerializedBody, outputStream); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/StringCodec.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/StringCodec.java new file mode 100644 index 00000000000..0deffe32b14 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/StringCodec.java @@ -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.rest.message.codec; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec; + +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +@Activate("string") +public class StringCodec implements HttpMessageCodec { + + + @Override + public Object decode(byte[] body, Class targetType) throws Exception { + return new String(body); + } + + @Override + public boolean contentTypeSupport(MediaType mediaType,Class targetType) { + return String.class.equals(targetType); + } + + + @Override + public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception { + outputStream.write(((String) unSerializedBody).getBytes(StandardCharsets.UTF_8)); + } + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/TextCodec.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/TextCodec.java new file mode 100644 index 00000000000..c4722d01b33 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/TextCodec.java @@ -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.rest.message.codec; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec; +import org.apache.dubbo.rpc.protocol.rest.message.MediaTypeMatcher; +import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils; + +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +@Activate("text") +public class TextCodec implements HttpMessageCodec { + + @Override + public Object decode(byte[] body, Class targetType) throws Exception { + return DataParseUtils.stringTypeConvert(targetType, new String(body, StandardCharsets.UTF_8)); + } + + + @Override + public boolean contentTypeSupport(MediaType mediaType, Class targetType) { + return MediaTypeMatcher.TEXT_PLAIN.mediaSupport(mediaType); + } + + @Override + public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception { + DataParseUtils.writeTextContent(unSerializedBody, outputStream); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/XMLCodec.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/XMLCodec.java new file mode 100644 index 00000000000..766ea8064b6 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/XMLCodec.java @@ -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.rest.message.codec; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec; +import org.apache.dubbo.rpc.protocol.rest.message.MediaTypeMatcher; + +import org.xml.sax.InputSource; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.Source; +import javax.xml.transform.sax.SAXSource; +import java.io.OutputStream; +import java.io.StringReader; + +@Activate("xml") +public class XMLCodec implements HttpMessageCodec { + + + @Override + public Object decode(byte[] body, Class targetType) throws Exception { + + + SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setFeature("http://xml.org/sax/features/external-general-entities", false); + spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + + // Do unmarshall operation + Source xmlSource = new SAXSource(spf.newSAXParser().getXMLReader(), new InputSource(new StringReader(new String(body)))); + + JAXBContext context = JAXBContext.newInstance(targetType); + Unmarshaller unmarshaller = context.createUnmarshaller(); + return unmarshaller.unmarshal(xmlSource); + + } + + @Override + public boolean contentTypeSupport(MediaType mediaType, Class targetType) { + return MediaTypeMatcher.TEXT_XML.mediaSupport(mediaType); + } + + + @Override + public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception { + Marshaller marshaller = JAXBContext.newInstance(unSerializedBody.getClass()).createMarshaller(); + marshaller.marshal(unSerializedBody, outputStream); + outputStream.write((byte[]) unSerializedBody); + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/DataParseUtils.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/DataParseUtils.java new file mode 100644 index 00000000000..d5e587cd93f --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/DataParseUtils.java @@ -0,0 +1,215 @@ +/* + * 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.rest.util; + +import org.apache.dubbo.common.utils.JsonUtils; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +public class DataParseUtils { + + public static Object stringTypeConvert(Class targetType, String value) { + + + if (targetType == Boolean.class) { + return Boolean.valueOf(value); + } + + if (targetType == String.class) { + return value; + } + + if (Number.class.isAssignableFrom(targetType)) { + return NumberUtils.parseNumber(value, targetType); + } + + return value; + + } + + + /** + * content-type text + * + * @param object + * @param outputStream + * @throws IOException + */ + public static void writeTextContent(Object object, OutputStream outputStream) throws IOException { + outputStream.write(objectTextConvertToByteArray(object)); + } + + /** + * content-type json + * + * @param object + * @param outputStream + * @throws Exception + */ + public static void writeJsonContent(Object object, OutputStream outputStream) throws Exception { + outputStream.write(JsonUtils.getJson().toJson(object).getBytes(StandardCharsets.UTF_8)); + } + + /** + * content-type form + * + * @param formData + * @param outputStream + * @throws Exception + */ + public static void writeFormContent(Map formData, OutputStream outputStream) throws Exception { + outputStream.write(serializeForm(formData, Charset.defaultCharset()).getBytes()); + } + + // TODO file multipart + + public static String serializeForm(Map formData, Charset charset) { + StringBuilder builder = new StringBuilder(); + formData.forEach((name, values) -> { + if (name == null) { + + return; + } + ((List) values).forEach(value -> { + try { + if (builder.length() != 0) { + builder.append('&'); + } + builder.append(URLEncoder.encode((String) name, charset.name())); + if (value != null) { + builder.append('='); + builder.append(URLEncoder.encode(String.valueOf(value), charset.name())); + } + } catch (UnsupportedEncodingException ex) { + throw new IllegalStateException(ex); + } + }); + }); + + return builder.toString(); + } + + public static byte[] objectTextConvertToByteArray(Object object) { + Class objectClass = object.getClass(); + + if (objectClass == Boolean.class) { + return object.toString().getBytes(); + } + + if (objectClass == String.class) { + return ((String) object).getBytes(); + } + + if (objectClass.isAssignableFrom(Number.class)) { + return (byte[]) NumberUtils.numberToBytes((Number) object); + } + + return object.toString().getBytes(); + + } + + public static byte[] objectJsonConvertToByteArray(Object object) { + Class objectClass = object.getClass(); + + if (objectClass == Boolean.class) { + return object.toString().getBytes(); + } + + if (objectClass == String.class) { + return ((String) object).getBytes(); + } + + if (objectClass.isAssignableFrom(Number.class)) { + return (byte[]) NumberUtils.numberToBytes((Number) object); + } + + return object.toString().getBytes(); + + } + + public static Object jsonConvert(Class targetType, byte[] body) throws Exception { + return JsonUtils.getJson().toJavaObject(new String(body, StandardCharsets.UTF_8), targetType); + } + + + public static Object multipartFormConvert(byte[] body, Charset charset) throws Exception { + String[] pairs = tokenizeToStringArray(new String(body, StandardCharsets.UTF_8), "&"); + Object result = MultiValueCreator.createMultiValueMap(); + for (String pair : pairs) { + int idx = pair.indexOf('='); + if (idx == -1) { + MultiValueCreator.add(result, URLDecoder.decode(pair, charset.name()), null); + } else { + String name = URLDecoder.decode(pair.substring(0, idx), charset.name()); + String value = URLDecoder.decode(pair.substring(idx + 1), charset.name()); + MultiValueCreator.add(result, name, value); + } + } + + return result; + } + + public static Object multipartFormConvert(byte[] body) throws Exception { + return multipartFormConvert(body, Charset.defaultCharset()); + } + + + public static String[] tokenizeToStringArray(String str, String delimiters) { + return tokenizeToStringArray(str, delimiters, true, true); + } + + public static String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, + boolean ignoreEmptyTokens) { + if (str == null) { + return null; + } else { + StringTokenizer st = new StringTokenizer(str, delimiters); + ArrayList tokens = new ArrayList(); + + while (true) { + String token; + do { + if (!st.hasMoreTokens()) { + return toStringArray(tokens); + } + + token = st.nextToken(); + if (trimTokens) { + token = token.trim(); + } + } while (ignoreEmptyTokens && token.length() <= 0); + + tokens.add(token); + } + } + } + + public static String[] toStringArray(Collection collection) { + return collection == null ? null : collection.toArray(new String[collection.size()]); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MediaTypeUtil.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MediaTypeUtil.java new file mode 100644 index 00000000000..4a16ea58618 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MediaTypeUtil.java @@ -0,0 +1,42 @@ +/* + * 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.rest.util; + +import org.apache.dubbo.metadata.rest.media.MediaType; +import org.apache.dubbo.rpc.protocol.rest.exception.UnSupportContentTypeException; + +import java.util.Arrays; +import java.util.List; + +public class MediaTypeUtil { + private static final List mediaTypes = Arrays.asList(MediaType.values()); + + public static MediaType convertMediaType(String... contentTypes) { + + for (String contentType : contentTypes) { + for (MediaType mediaType : mediaTypes) { + + if (contentType != null && contentType.contains(mediaType.value)) { + return mediaType; + } + } + } + + throw new UnSupportContentTypeException(Arrays.toString(contentTypes)); + + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MultiValueCreator.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MultiValueCreator.java new file mode 100644 index 00000000000..5c6bf2ac9e3 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MultiValueCreator.java @@ -0,0 +1,55 @@ +/* + * 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.rest.util; + + +import java.lang.reflect.Method; + + +public class MultiValueCreator { + private final static String SPRING_MultiValueMap = "org.springframework.util.LinkedMultiValueMap"; + private final static String JAVAX_MultiValueMap = "org.jboss.resteasy.specimpl.MultivaluedMapImpl"; + + private static Class multiValueMapClass = null; + private static Method multiValueMapAdd = null; + + static { + multiValueMapClass = ReflectUtils.findClassTryException(SPRING_MultiValueMap, JAVAX_MultiValueMap); + multiValueMapAdd = ReflectUtils.getMethodAndTryCatch(multiValueMapClass, "add", new Class[]{Object.class, Object.class}); + } + + + public static Object createMultiValueMap() { + try { + return multiValueMapClass.newInstance(); + } catch (Exception e) { + + } + + return null; + } + + public static void add(Object multiValueMap, String key, String value) { + try { + ReflectUtils.invokeAndTryCatch(multiValueMap, multiValueMapAdd, new String[]{key, value}); + } catch (Exception e) { + + } + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/NumberUtils.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/NumberUtils.java new file mode 100644 index 00000000000..47aa746bbb3 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/NumberUtils.java @@ -0,0 +1,149 @@ +/* + * 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.rest.util; + +import org.apache.dubbo.common.utils.Assert; +import org.apache.dubbo.common.utils.StringUtils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class NumberUtils { + + + public static final Set> STANDARD_NUMBER_TYPES; + + static { + Set> numberTypes = new HashSet<>(8); + numberTypes.add(Byte.class); + numberTypes.add(Short.class); + numberTypes.add(Integer.class); + numberTypes.add(Long.class); + numberTypes.add(BigInteger.class); + numberTypes.add(Float.class); + numberTypes.add(Double.class); + numberTypes.add(BigDecimal.class); + STANDARD_NUMBER_TYPES = Collections.unmodifiableSet(numberTypes); + } + + + public static T parseNumber(String text, Class targetClass) { + Assert.notNull(text, "Text must not be null"); + Assert.notNull(targetClass, "Target class must not be null"); + String trimmed = trimAllWhitespace(text); + + if (Byte.class == targetClass) { + return (T) (isHexNumber(trimmed) ? Byte.decode(trimmed) : Byte.valueOf(trimmed)); + } else if (Short.class == targetClass) { + return (T) (isHexNumber(trimmed) ? Short.decode(trimmed) : Short.valueOf(trimmed)); + } else if (Integer.class == targetClass) { + return (T) (isHexNumber(trimmed) ? Integer.decode(trimmed) : Integer.valueOf(trimmed)); + } else if (Long.class == targetClass) { + return (T) (isHexNumber(trimmed) ? Long.decode(trimmed) : Long.valueOf(trimmed)); + } else if (BigInteger.class == targetClass) { + return (T) (isHexNumber(trimmed) ? decodeBigInteger(trimmed) : new BigInteger(trimmed)); + } else if (Float.class == targetClass) { + return (T) Float.valueOf(trimmed); + } else if (Double.class == targetClass) { + return (T) Double.valueOf(trimmed); + } else if (BigDecimal.class == targetClass || Number.class == targetClass) { + return (T) new BigDecimal(trimmed); + } else { + throw new IllegalArgumentException( + "Cannot convert String [" + text + "] to target class [" + targetClass.getName() + "]"); + } + } + + + private static boolean isHexNumber(String value) { + int index = (value.startsWith("-") ? 1 : 0); + return (value.startsWith("0x", index) || value.startsWith("0X", index) || value.startsWith("#", index)); + } + + + private static BigInteger decodeBigInteger(String value) { + int radix = 10; + int index = 0; + boolean negative = false; + + // Handle minus sign, if present. + if (value.startsWith("-")) { + negative = true; + index++; + } + + // Handle radix specifier, if present. + if (value.startsWith("0x", index) || value.startsWith("0X", index)) { + index += 2; + radix = 16; + } else if (value.startsWith("#", index)) { + index++; + radix = 16; + } else if (value.startsWith("0", index) && value.length() > 1 + index) { + index++; + radix = 8; + } + + BigInteger result = new BigInteger(value.substring(index), radix); + return (negative ? result.negate() : result); + } + + public static String trimAllWhitespace(String str) { + if (StringUtils.isEmpty(str)) { + return str; + } + + int len = str.length(); + StringBuilder sb = new StringBuilder(str.length()); + for (int i = 0; i < len; i++) { + char c = str.charAt(i); + if (!Character.isWhitespace(c)) { + sb.append(c); + } + } + return sb.toString(); + } + + + public static Object numberToBytes(Number number) { + + if (number instanceof Byte) { + // Use default encoding. + return Byte.toString(number.byteValue()).getBytes(); + } else if (number instanceof Double) { + return Double.toString(number.doubleValue()).getBytes(); + } else if (number instanceof Float) { + return Float.toString(number.floatValue()).getBytes(); + } else if (number instanceof Integer) { + return Float.toString(number.intValue()).getBytes(); + } else if (number instanceof Long) { + return Long.toString(number.longValue()).getBytes(); + } else if (number instanceof Short) { + return Short.toString(number.shortValue()).getBytes(); + } else if (number instanceof BigDecimal) { + return BigDecimal.class.cast(number).toString().getBytes(); + } + + return number; + + + } + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/ReflectUtils.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/ReflectUtils.java new file mode 100644 index 00000000000..23b71540e73 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/ReflectUtils.java @@ -0,0 +1,269 @@ +/* + * 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.rest.util; + + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +public class ReflectUtils { + private final static Class[] EMPTY_CLASS_ARRAY = new Class[0]; + private final static Object[] EMPTY_OBJECT_ARRAY = new Object[0]; + + public static Field getField(Class clazz, String field) throws IllegalAccessException { + Field[] fields = clazz.getDeclaredFields(); + + for (Field field1 : fields) { + if (field1.getName().equals(field)) { + return setModifiersUnFinal(field1); + } + } + + fields = clazz.getFields(); + + for (Field field1 : fields) { + if (field1.getName().equals(field)) { + return setModifiersUnFinal(field1); + } + } + + return null; + } + + + public static Method getMethod(Class clazz, String method, Class[] paramTypes) throws NoSuchMethodException { + Method declaredMethod = clazz.getDeclaredMethod(method, paramTypes); + declaredMethod.setAccessible(true); + return declaredMethod; + } + + private static Field setModifiersUnFinal(Field field) throws IllegalAccessException { + // public +// MODIFIERS.set(field, 1); + field.setAccessible(true); + return field; + } + + public static Class findClass(String name) throws ClassNotFoundException { + + return findClass(Thread.currentThread().getContextClassLoader(), name); + + } + + public static Class findClass(String name, ClassLoader classLoader) throws ClassNotFoundException { + + return classLoader.loadClass(name); + + } + + public static Class findClassAndTryCatch(String name, ClassLoader classLoader) { + + try { + return findClass(name, classLoader); + } catch (Throwable e) { + + } + return null; + + } + + public static Class findClass(String... name) throws ClassNotFoundException { + + return findClass(Thread.currentThread().getContextClassLoader(), name); + } + + + public static Class findClass(ClassLoader classLoader, String... name) throws ClassNotFoundException { + + String[] names = name; + + Class tmp; + for (String s : names) { + tmp = findClassAndTryCatch(s, classLoader); + if (tmp == null) { + continue; + } else { + return tmp; + } + } + throw new ClassNotFoundException(); + } + + + public static Class findClassTryException(ClassLoader classLoader, String... name) { + + try { + return findClass(classLoader, name); + } catch (Exception e) { + + } + return null; + + } + + public static Class findClassTryException(String... name) { + return findClassTryException(Thread.currentThread().getContextClassLoader(), name); + } + + + public static Object getArrayElement(Object obj, int index) { + return Array.get(obj, index); + } + + public static List getArrayElements(Object obj) { + + List objects = new ArrayList(); + int length = Array.getLength(obj); + + if (length == 0) { + return objects; + } + + for (int i = 0; i < length; i++) { + objects.add(Array.get(obj, i)); + } + + return objects; + } + + + public static Field getFieldAndTryCatch(Class clazz, String field) { + + try { + return getField(clazz, field); + } catch (Exception e) { + + } + return null; + } + + public static Method getMethod(Class clazz, String method) throws NoSuchMethodException { + Method declaredMethod = clazz.getDeclaredMethod(method, new Class[]{}); + declaredMethod.setAccessible(true); + return declaredMethod; + } + + public static Method getMethodAndTry(Class clazz, String method) { + Method declaredMethod = null; + try { + declaredMethod = clazz.getDeclaredMethod(method, EMPTY_CLASS_ARRAY); + declaredMethod.setAccessible(true); + } catch (Exception e) { + + } + + if (declaredMethod == null) { + try { + declaredMethod = getMethodByName(clazz, method); + } catch (Exception e) { + + } + } + + return declaredMethod; + } + + public static Object invoke(Object object, Method method) { + try { + return method.invoke(object, EMPTY_OBJECT_ARRAY); + } catch (Exception e) { + return null; + } + } + + public static Object invoke(Object object, Method method, Object[] params) throws InvocationTargetException, IllegalAccessException { + return method.invoke(object, params); + + } + + public static Object invokeAndTryCatch(Object object, Method method, Object[] params) { + try { + return invoke(object, method, params); + } catch (Exception e) { + + } + + return null; + } + + public static Class findClassAndTry(String name) { + + try { + return findClass(name); + } catch (Exception e) { + return null; + } + + } + + public static Method getMethodByName(Class clazz, String name) { + Method[] declaredMethods = clazz.getMethods(); + + for (Method declaredMethod : declaredMethods) { + if (name.equals(declaredMethod.getName())) { + + return declaredMethod; + } + } + + return null; + + } + + + public static Object getValueByFields(Object obj, Field... fields) { + for (Field field : fields) { + + try { + Object o = field.get(obj); + if (o != null) { + return o; + } + } catch (Exception e) { + + } + + } + + return null; + } + + public static Method getMethodAndTryCatch(Class clazz, String method, Class[] paramTypes) { + try { + return getMethod(clazz, method, paramTypes); + } catch (Throwable e) { + + } + return null; + + } + + + public static Object getFieldValueAndTryCatch(Object obj, String field) { + try { + return ReflectUtils.getValueByFields(obj, ReflectUtils.getFieldAndTryCatch(obj.getClass(), field)); + } catch (Exception e) { + return null; + } + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/StreamUtils.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/StreamUtils.java new file mode 100644 index 00000000000..5b597c17243 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/StreamUtils.java @@ -0,0 +1,41 @@ +/* + * 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.rest.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; + +public class StreamUtils { + public static String copyToString(InputStream in, Charset charset) throws IOException { + if (in == null) { + return ""; + } else { + StringBuilder out = new StringBuilder(); + InputStreamReader reader = new InputStreamReader(in, charset); + char[] buffer = new char[4096]; + + int bytesRead; + while ((bytesRead = reader.read(buffer)) != -1) { + out.append(buffer, 0, bytesRead); + } + + return out.toString(); + } + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/TypeUtil.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/TypeUtil.java new file mode 100644 index 00000000000..9d768affc23 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/TypeUtil.java @@ -0,0 +1,38 @@ +/* + * 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.rest.util; + +public class TypeUtil { + + public static boolean isNumber(Class clazz) { + return Number.class.isAssignableFrom(clazz); + } + + public static boolean isPrimitive(Class clazz) { + return clazz.isPrimitive(); + } + + public static boolean isString(Class clazz) { + return clazz == String.class; + } + + public static boolean isNumberType(Class clazz) { + return clazz.isPrimitive() || isNumber(clazz); + } + + +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept b/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept new file mode 100644 index 00000000000..e277b45107b --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept @@ -0,0 +1,6 @@ +must-intercept=org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept.AddMustAttachmentIntercept +attachment=org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept.AttachmentIntercept +serialize=org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept.SerializeBodyIntercept +path=org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept.PathVariableIntercept +header=org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept.RequestHeaderIntercept +paramparse=org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept.ParamParseIntercept diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BaseConsumerParamParser b/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BaseConsumerParamParser new file mode 100644 index 00000000000..44bcfc15c38 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BaseConsumerParamParser @@ -0,0 +1,5 @@ +consumer-body=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BodyConsumerParamParser +consumer-header=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.HeaderConsumerParamParser +consumer-req=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.ReqOrResConsumerParamParser +consumer-parameter=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.ParameterConsumerParamParser +consumer-form=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.FormConsumerParamParser diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec b/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec new file mode 100644 index 00000000000..11a7b325399 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec @@ -0,0 +1,6 @@ +multiValue=org.apache.dubbo.rpc.protocol.rest.message.codec.MultiValueCodec +text=org.apache.dubbo.rpc.protocol.rest.message.codec.TextCodec +json=org.apache.dubbo.rpc.protocol.rest.message.codec.JsonCodec +string=org.apache.dubbo.rpc.protocol.rest.message.codec.StringCodec +byteArray=org.apache.dubbo.rpc.protocol.rest.message.codec.ByteArrayCodec +xml=org.apache.dubbo.rpc.protocol.rest.message.codec.XMLCodec diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoService.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoService.java index bb9e4a8f1f7..265c6ce382f 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoService.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoService.java @@ -16,26 +16,23 @@ */ package org.apache.dubbo.rpc.protocol.rest; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; -@Path("/demoService") +@RestController() +@RequestMapping("/demoService") public interface DemoService { - @GET - @Path("/hello") - Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b); + @RequestMapping(value = "/hello", method = RequestMethod.GET) + Integer hello(@RequestParam Integer a, @RequestParam Integer b); - @GET - @Path("/error") + @RequestMapping(value = "/error", method = RequestMethod.GET) String error(); - @POST - @Path("/say") - @Consumes({MediaType.TEXT_PLAIN}) - String sayHello(String name); + @RequestMapping(value = "/say", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE) + String sayHello(@RequestBody String name); } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoServiceImpl.java index 3cf96227406..a71fb9f45b1 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoServiceImpl.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoServiceImpl.java @@ -19,12 +19,22 @@ import org.apache.dubbo.rpc.RpcContext; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; import java.util.Map; - +@Path("/demoService") public class DemoServiceImpl implements DemoService { private static Map context; private boolean called; + @POST + @Path("/say") + @Consumes({MediaType.TEXT_PLAIN}) + @Override public String sayHello(String name) { called = true; return "Hello, " + name; @@ -35,12 +45,17 @@ public boolean isCalled() { return called; } + @GET + @Path("/hello") @Override - public Integer hello(Integer a, Integer b) { + public Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b) { context = RpcContext.getServerAttachment().getObjectAttachments(); return a + b; } + + @GET + @Path("/error") @Override public String error() { throw new RuntimeException(); diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/JaxrsRestProtocolTest.java similarity index 85% rename from dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java rename to dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/JaxrsRestProtocolTest.java index 758c7fb9002..3c4510056bd 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/JaxrsRestProtocolTest.java @@ -33,11 +33,14 @@ import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.model.ServiceDescriptor; +import org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestService; +import org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestServiceImpl; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.Arrays; import java.util.Map; import static org.apache.dubbo.remoting.Constants.SERVER_KEY; @@ -47,7 +50,7 @@ import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -class RestProtocolTest { +class JaxrsRestProtocolTest { private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("rest"); private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); private final int availablePort = NetUtils.getAvailablePort(); @@ -62,7 +65,8 @@ public void tearDown() { @Test void testRestProtocol() { - URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort() + "/rest/say?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.DemoService"); + URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.DemoService"); + DemoServiceImpl server = new DemoServiceImpl(); url = this.registerProvider(url, server, DemoService.class); @@ -79,6 +83,38 @@ void testRestProtocol() { exporter.unexport(); } + + @Test + void testAnotherUserRestProtocol() { + URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestService"); + + AnotherUserRestServiceImpl server = new AnotherUserRestServiceImpl(); + + url = this.registerProvider(url, server, DemoService.class); + + Exporter exporter = protocol.export(proxy.getInvoker(server, AnotherUserRestService.class, url)); + Invoker invoker = protocol.refer(AnotherUserRestService.class, url); + + + AnotherUserRestService client = proxy.getProxy(invoker); + User result = client.getUser(123l); + + Assertions.assertEquals(123l, result.getId()); + + result.setName("dubbo"); + Assertions.assertEquals(123l, client.registerUser(result).getId()); + + Assertions.assertEquals("context", client.getContext()); + + byte[] bytes = {1, 2, 3, 4}; + Assertions.assertTrue(Arrays.equals(bytes, client.bytes(bytes))); + + Assertions.assertEquals(1l, client.number(1l)); + + invoker.destroy(); + exporter.unexport(); + } + @Test void testRestProtocolWithContextPath() { DemoServiceImpl server = new DemoServiceImpl(); @@ -184,7 +220,7 @@ void testFilter() { URL url = this.registerProvider(exportUrl, server, DemoService.class); URL nettyUrl = url.addParameter(SERVER_KEY, "netty") - .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter"); + .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter"); Exporter exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); @@ -204,7 +240,7 @@ void testRpcContextFilter() { // use RpcContextFilter URL nettyUrl = url.addParameter(SERVER_KEY, "netty") - .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.RpcContextFilter"); + .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.RpcContextFilter"); Exporter exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl)); DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/SpringMvcRestProtocolTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/SpringMvcRestProtocolTest.java new file mode 100644 index 00000000000..fb63dea8db2 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/SpringMvcRestProtocolTest.java @@ -0,0 +1,278 @@ +/* + * 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.rest; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.rpc.Exporter; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Protocol; +import org.apache.dubbo.rpc.ProxyFactory; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.FrameworkModel; +import org.apache.dubbo.rpc.model.ModuleServiceRepository; +import org.apache.dubbo.rpc.model.ProviderModel; +import org.apache.dubbo.rpc.model.ServiceDescriptor; +import org.apache.dubbo.rpc.protocol.rest.rest.RestDemoService; +import org.apache.dubbo.rpc.protocol.rest.rest.RestDemoServiceImpl; + +import org.hamcrest.CoreMatchers; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.apache.dubbo.remoting.Constants.SERVER_KEY; +import static org.apache.dubbo.rpc.protocol.rest.Constants.EXTENSION_KEY; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +public class SpringMvcRestProtocolTest { + private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("rest"); + private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); + private final int availablePort = NetUtils.getAvailablePort(); + private final URL exportUrl = URL.valueOf("rest://127.0.0.1:" + availablePort + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.DemoService"); + private final ModuleServiceRepository repository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository(); + + @AfterEach + public void tearDown() { + protocol.destroy(); + FrameworkModel.destroyAll(); + } + + public RestDemoService getServerImpl() { + return new RestDemoServiceImpl(); + } + + + public Class getServerClass() { + return RestDemoService.class; + } + + public Exporter getExport(URL url, RestDemoService server) { + return protocol.export(proxy.getInvoker(server, getServerClass(), url)); + } + + + @Test + void testRestProtocol() { + URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.DemoService"); + + RestDemoService server = getServerImpl(); + + url = this.registerProvider(url, server, getServerClass()); + + Exporter exporter = getExport(url, server); + Invoker invoker = protocol.refer(DemoService.class, url); + Assertions.assertFalse(server.isCalled()); + + DemoService client = proxy.getProxy(invoker); + String result = client.sayHello("haha"); + Assertions.assertTrue(server.isCalled()); + Assertions.assertEquals("Hello, haha", result); + invoker.destroy(); + exporter.unexport(); + } + + @Test + void testRestProtocolWithContextPath() { + RestDemoService server = getServerImpl(); + Assertions.assertFalse(server.isCalled()); + int port = NetUtils.getAvailablePort(); + URL url = URL.valueOf("rest://127.0.0.1:" + port + "/a/b/c?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.DemoService"); + + url = this.registerProvider(url, server, DemoService.class); + + Exporter exporter = getExport(url, server); + + url = URL.valueOf("rest://127.0.0.1:" + port + "/a/b/c/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.DemoService"); + Invoker invoker = protocol.refer(DemoService.class, url); + DemoService client = proxy.getProxy(invoker); + String result = client.sayHello("haha"); + Assertions.assertTrue(server.isCalled()); + Assertions.assertEquals("Hello, haha", result); + invoker.destroy(); + exporter.unexport(); + } + + @Test + void testExport() { + RestDemoService server = getServerImpl(); + + URL url = this.registerProvider(exportUrl, server, DemoService.class); + + RpcContext.getClientAttachment().setAttachment("timeout", "200"); + Exporter exporter = getExport(url, server); + + DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, url)); + + Integer echoString = demoService.hello(1, 2); + assertThat(echoString, is(3)); + + exporter.unexport(); + } + + @Test + void testNettyServer() { + RestDemoService server = getServerImpl(); + + URL url = this.registerProvider(exportUrl, server, DemoService.class); + + URL nettyUrl = url.addParameter(SERVER_KEY, "netty"); + + Exporter exporter = protocol.export(proxy.getInvoker(server, RestDemoService.class, nettyUrl)); + + DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); + + Integer echoString = demoService.hello(10, 10); + assertThat(echoString, is(20)); + + exporter.unexport(); + } + + @Test + void testServletWithoutWebConfig() { + Assertions.assertThrows(RpcException.class, () -> { + RestDemoService server = getServerImpl(); + + URL url = this.registerProvider(exportUrl, server, DemoService.class); + + URL servletUrl = url.addParameter(SERVER_KEY, "servlet"); + + protocol.export(proxy.getInvoker(server, getServerClass(), servletUrl)); + }); + } + + @Test + void testErrorHandler() { + Assertions.assertThrows(RpcException.class, () -> { + RestDemoService server = getServerImpl(); + + URL url = this.registerProvider(exportUrl, server, DemoService.class); + + URL nettyUrl = url.addParameter(SERVER_KEY, "netty"); + Exporter exporter = getExport(nettyUrl, server); + + DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); + + demoService.error(); + }); + } + + @Test + void testInvoke() { + RestDemoService server = getServerImpl(); + + URL url = this.registerProvider(exportUrl, server, DemoService.class); + + Exporter exporter = getExport(url, server); + + RpcInvocation rpcInvocation = new RpcInvocation("hello", DemoService.class.getName(), "", new Class[]{Integer.class, Integer.class}, new Integer[]{2, 3}); + + Result result = exporter.getInvoker().invoke(rpcInvocation); + assertThat(result.getValue(), CoreMatchers.is(5)); + } + + @Test + void testFilter() { + RestDemoService server = getServerImpl(); + + URL url = this.registerProvider(exportUrl, server, DemoService.class); + + URL nettyUrl = url.addParameter(SERVER_KEY, "netty") + .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter"); + Exporter exporter = getExport(nettyUrl, server); + + DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); + + Integer result = demoService.hello(1, 2); + + assertThat(result, is(3)); + + exporter.unexport(); + } + + @Test + void testRpcContextFilter() { + RestDemoService server = getServerImpl(); + + URL url = this.registerProvider(exportUrl, server, DemoService.class); + + // use RpcContextFilter + URL nettyUrl = url.addParameter(SERVER_KEY, "netty") + .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.RpcContextFilter"); + Exporter exporter = protocol.export(proxy.getInvoker(server, RestDemoService.class, nettyUrl)); + + DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl)); + + // make sure null and base64 encoded string can work + RpcContext.getClientAttachment().setAttachment("key1", null); + RpcContext.getClientAttachment().setAttachment("key2", "value"); + RpcContext.getClientAttachment().setAttachment("key3", "=value"); + RpcContext.getClientAttachment().setAttachment("key4", "YWJjZGVmCg=="); + RpcContext.getClientAttachment().setAttachment("key5", "val=ue"); + Integer result = demoService.hello(1, 2); + + assertThat(result, is(3)); + + Map attachment = RestDemoServiceImpl.getAttachments(); + assertThat(attachment.get("key1"), nullValue()); + assertThat(attachment.get("key2"), equalTo("value")); + assertThat(attachment.get("key3"), equalTo("=value")); + assertThat(attachment.get("key4"), equalTo("YWJjZGVmCg==")); + assertThat(attachment.get("key5"), equalTo("val=ue")); + + exporter.unexport(); + } + + @Test + void testRegFail() { + Assertions.assertThrows(RuntimeException.class, () -> { + RestDemoService server = getServerImpl(); + + URL url = this.registerProvider(exportUrl, server, DemoService.class); + + URL nettyUrl = url.addParameter(EXTENSION_KEY, "com.not.existing.Filter"); + Exporter exporter = getExport(nettyUrl, server); + }); + } + + @Test + void testDefaultPort() { + assertThat(protocol.getDefaultPort(), is(80)); + } + + private URL registerProvider(URL url, Object impl, Class interfaceClass) { + ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass); + ProviderModel providerModel = new ProviderModel( + url.getServiceKey(), + impl, + serviceDescriptor, + null, + null); + repository.registerProvider(providerModel); + return url.setServiceModel(providerModel); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/User.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/User.java new file mode 100644 index 00000000000..668d8fcafdb --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/User.java @@ -0,0 +1,62 @@ +/* + * 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.rest; + +import java.io.Serializable; + +/** + * User Entity + * + * @since 2.7.6 + */ +public class User implements Serializable { + + private Long id; + + private String name; + + private Integer age; + + 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 Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + @Override + public String toString() { + return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/DemoService.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/DemoService.java new file mode 100644 index 00000000000..346ae90184a --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/DemoService.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.rest.mvc; + + +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController("/demoService") +public interface DemoService { + @RequestMapping(value = "/hello", method = RequestMethod.GET) + Integer hello(@RequestParam Integer a, @RequestParam Integer b); + + @RequestMapping(value = "/error", method = RequestMethod.GET) + String error(); + + @RequestMapping(value = "/sayHello", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE) + String sayHello(String name); +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringDemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringDemoServiceImpl.java new file mode 100644 index 00000000000..72bf284cf03 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringDemoServiceImpl.java @@ -0,0 +1,55 @@ +/* + * 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.rest.mvc; + + +import org.apache.dubbo.rpc.RpcContext; + +import java.util.Map; + +public class SpringDemoServiceImpl implements DemoService { + private static Map context; + private boolean called; + + + @Override + public String sayHello(String name) { + called = true; + return "Hello, " + name; + } + + + public boolean isCalled() { + return called; + } + + @Override + public Integer hello(Integer a, Integer b) { + context = RpcContext.getServerAttachment().getObjectAttachments(); + return a + b; + } + + + @Override + public String error() { + throw new RuntimeException(); + } + + public static Map getAttachments() { + return context; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestService.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestService.java new file mode 100644 index 00000000000..837fdf27c82 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestService.java @@ -0,0 +1,57 @@ +/* + * 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.rest.rest; + +import org.apache.dubbo.rpc.protocol.rest.User; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("u") +@Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) +public interface AnotherUserRestService { + + @GET + @Path("{id : \\d+}") + @Produces({MediaType.APPLICATION_JSON}) + User getUser(@PathParam("id") Long id); + + @POST + @Path("register") + @Produces("text/xml; charset=UTF-8") + RegistrationResult registerUser(User user); + + @GET + @Path("context") + @Produces({MediaType.APPLICATION_JSON}) + String getContext(); + + @POST + @Path("bytes") + @Produces({MediaType.APPLICATION_JSON}) + byte[] bytes(byte[] bytes); + + @POST + @Path("number") + @Produces({MediaType.APPLICATION_JSON}) + Long number(Long number); +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestServiceImpl.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestServiceImpl.java new file mode 100644 index 00000000000..42223ebdda3 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestServiceImpl.java @@ -0,0 +1,53 @@ +/* + * 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.rest.rest; + +import org.apache.dubbo.rpc.protocol.rest.User; + + +public class AnotherUserRestServiceImpl implements AnotherUserRestService { + + + @Override + public User getUser(Long id) { + + User user = new User(); + user.setId(id); + return user; + } + + @Override + public RegistrationResult registerUser(User user) { + return new RegistrationResult(user.getId()); + } + + @Override + public String getContext() { + + return "context"; + } + + @Override + public byte[] bytes(byte[] bytes) { + return bytes; + } + + @Override + public Long number(Long number) { + return number; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RegistrationResult.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RegistrationResult.java new file mode 100644 index 00000000000..7f48b95c713 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RegistrationResult.java @@ -0,0 +1,44 @@ +/* + * 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.rest.rest; + +import javax.xml.bind.annotation.XmlRootElement; +import java.io.Serializable; + +/** + * DTO to customize the returned message + */ +@XmlRootElement +public class RegistrationResult implements Serializable { + + private Long id; + + public RegistrationResult() { + } + + public RegistrationResult(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java new file mode 100644 index 00000000000..56384d0725e --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java @@ -0,0 +1,43 @@ +/* + * 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.rest.rest; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + + +@Path("/demoService") +public interface RestDemoService { + @GET + @Path("/hello") + Integer hello(@QueryParam("a")Integer a,@QueryParam("b") Integer b); + + @GET + @Path("/error") + String error(); + + @POST + @Path("/say") + @Consumes({MediaType.TEXT_PLAIN}) + String sayHello(String name); + + boolean isCalled(); +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java new file mode 100644 index 00000000000..e9819e5db6f --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java @@ -0,0 +1,55 @@ +/* + * 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.rest.rest; + +import org.apache.dubbo.rpc.RpcContext; + +import java.util.Map; + + +public class RestDemoServiceImpl implements RestDemoService { + private static Map context; + private boolean called; + + + @Override + public String sayHello(String name) { + called = true; + return "Hello, " + name; + } + + + public boolean isCalled() { + return called; + } + + @Override + public Integer hello(Integer a, Integer b) { + context = RpcContext.getServerAttachment().getObjectAttachments(); + return a + b; + } + + + @Override + public String error() { + throw new RuntimeException(); + } + + public static Map getAttachments() { + return context; + } +} diff --git a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/SpringmvcDemoService.java b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/SpringmvcDemoService.java new file mode 100644 index 00000000000..18726a05d1a --- /dev/null +++ b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/SpringmvcDemoService.java @@ -0,0 +1,21 @@ +/* + * 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.test.common.api; + + +public interface SpringmvcDemoService { +} From 6a8ae5c61e631f12eb20323936a0fa7587a30721 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 19:53:34 +0800 Subject: [PATCH 02/12] Bump micrometer-tracing-bom from 1.0.1 to 1.0.2 (#11607) Bumps [micrometer-tracing-bom](https://github.com/micrometer-metrics/tracing) from 1.0.1 to 1.0.2. - [Release notes](https://github.com/micrometer-metrics/tracing/releases) - [Commits](https://github.com/micrometer-metrics/tracing/compare/v1.0.1...v1.0.2) --- updated-dependencies: - dependency-name: io.micrometer:micrometer-tracing-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dubbo-dependencies-bom/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml index cc437deab39..5b70f4ce3da 100644 --- a/dubbo-dependencies-bom/pom.xml +++ b/dubbo-dependencies-bom/pom.xml @@ -135,7 +135,7 @@ 1.8.0 0.1.35 1.10.3 - 1.0.1 + 1.0.2 3.3 0.16.0 1.0.4 From 067cb8d79b25fd4e4318cb32df0e26fe7b62553e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 19:53:50 +0800 Subject: [PATCH 03/12] Bump maven-javadoc-plugin from 3.4.1 to 3.5.0 (#11608) Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.4.1 to 3.5.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.4.1...maven-javadoc-plugin-3.5.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dfec4c3efb8..8336757dd63 100644 --- a/pom.xml +++ b/pom.xml @@ -118,7 +118,7 @@ 2.8.2 3.10.1 3.2.1 - 3.4.1 + 3.5.0 9.4.50.v20221201 3.2.1 0.8.8 From 3214940eb7abff07ea8da1b09f2fa4c44a8c2526 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 19:54:10 +0800 Subject: [PATCH 04/12] Bump byte-buddy from 1.13.0 to 1.14.0 (#11609) Bumps [byte-buddy](https://github.com/raphw/byte-buddy) from 1.13.0 to 1.14.0. - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.13.0...byte-buddy-1.14.0) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dubbo-dependencies-bom/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml index 5b70f4ce3da..b3615577eaf 100644 --- a/dubbo-dependencies-bom/pom.xml +++ b/dubbo-dependencies-bom/pom.xml @@ -94,7 +94,7 @@ 5.3.25 5.8.1 3.29.2-GA - 1.13.0 + 1.14.0 3.2.10.Final 4.1.87.Final 2.2.1 From 17258a996de283bd3931df473e4a6115ceb8be29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 19:54:20 +0800 Subject: [PATCH 05/12] Bump reactor-core from 3.5.2 to 3.5.3 (#11610) Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.5.2 to 3.5.3. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.5.2...v3.5.3) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dubbo-dependencies-bom/pom.xml | 2 +- dubbo-rpc/dubbo-rpc-triple/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml index b3615577eaf..77a7a24d9f6 100644 --- a/dubbo-dependencies-bom/pom.xml +++ b/dubbo-dependencies-bom/pom.xml @@ -139,7 +139,7 @@ 3.3 0.16.0 1.0.4 - 3.5.2 + 3.5.3 4.10.0 2.1.1 diff --git a/dubbo-rpc/dubbo-rpc-triple/pom.xml b/dubbo-rpc/dubbo-rpc-triple/pom.xml index d6e08e56115..7e6cf1cfdcf 100644 --- a/dubbo-rpc/dubbo-rpc-triple/pom.xml +++ b/dubbo-rpc/dubbo-rpc-triple/pom.xml @@ -31,7 +31,7 @@ false 0.0.4.1-SNAPSHOT 1.0.4 - 3.5.2 + 3.5.3 3.21.12 From 77724f3b7d86d3712abdcf34fdb6182c7b539976 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 19:54:35 +0800 Subject: [PATCH 06/12] Bump maven-failsafe-plugin from 3.0.0-M8 to 3.0.0-M9 (#11612) Bumps [maven-failsafe-plugin](https://github.com/apache/maven-surefire) from 3.0.0-M8 to 3.0.0-M9. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.0.0-M8...surefire-3.0.0-M9) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-failsafe-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8336757dd63..047e0eff64b 100644 --- a/pom.xml +++ b/pom.xml @@ -114,7 +114,7 @@ 3.3.0 3.0.0-M8 - 3.0.0-M8 + 3.0.0-M9 2.8.2 3.10.1 3.2.1 From 77a4e16ec37e083fce024b1bf33a919cd05c3635 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 19:54:49 +0800 Subject: [PATCH 07/12] Bump libthrift from 0.17.0 to 0.18.0 (#11614) Bumps [libthrift](https://github.com/apache/thrift) from 0.17.0 to 0.18.0. - [Release notes](https://github.com/apache/thrift/releases) - [Changelog](https://github.com/apache/thrift/blob/master/CHANGES.md) - [Commits](https://github.com/apache/thrift/compare/v0.17.0...v0.18.0) --- updated-dependencies: - dependency-name: org.apache.thrift:libthrift dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dubbo-dependencies-bom/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml index 77a7a24d9f6..db9034f7941 100644 --- a/dubbo-dependencies-bom/pom.xml +++ b/dubbo-dependencies-bom/pom.xml @@ -112,7 +112,7 @@ 1.5.3 1.4.3 3.5.5 - 0.17.0 + 0.18.0 4.0.66 3.19.6 1.3.2 From 57ab2819a35fcabe6c44d366e08843231443a8aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 20:09:31 +0800 Subject: [PATCH 08/12] Bump micrometer-bom from 1.10.3 to 1.10.4 (#11611) Bumps [micrometer-bom](https://github.com/micrometer-metrics/micrometer) from 1.10.3 to 1.10.4. - [Release notes](https://github.com/micrometer-metrics/micrometer/releases) - [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.10.3...v1.10.4) --- updated-dependencies: - dependency-name: io.micrometer:micrometer-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dubbo-dependencies-bom/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml index db9034f7941..d470ea02e31 100644 --- a/dubbo-dependencies-bom/pom.xml +++ b/dubbo-dependencies-bom/pom.xml @@ -134,7 +134,7 @@ 3.12.0 1.8.0 0.1.35 - 1.10.3 + 1.10.4 1.0.2 3.3 0.16.0 From b2353ad9ff8d9faa92cbd7748c5df7242b5516a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 20:09:42 +0800 Subject: [PATCH 09/12] Bump maven-surefire-plugin from 3.0.0-M8 to 3.0.0-M9 (#11613) Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.0.0-M8 to 3.0.0-M9. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.0.0-M8...surefire-3.0.0-M9) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 047e0eff64b..be793127bb0 100644 --- a/pom.xml +++ b/pom.xml @@ -113,7 +113,7 @@ UTF-8 3.3.0 - 3.0.0-M8 + 3.0.0-M9 3.0.0-M9 2.8.2 3.10.1 From b62883c17792c60656c5959e9804438557b5dd31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 20:09:55 +0800 Subject: [PATCH 10/12] Bump protobuf-java from 3.21.12 to 3.22.0 (#11615) Bumps [protobuf-java](https://github.com/protocolbuffers/protobuf) from 3.21.12 to 3.22.0. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/generate_changelog.py) - [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.21.12...v3.22.0) --- updated-dependencies: - dependency-name: com.google.protobuf:protobuf-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dubbo-demo/dubbo-demo-triple/pom.xml | 2 +- dubbo-rpc/dubbo-rpc-triple/pom.xml | 2 +- dubbo-xds/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dubbo-demo/dubbo-demo-triple/pom.xml b/dubbo-demo/dubbo-demo-triple/pom.xml index 2ae860a5bde..f420d80d6b3 100644 --- a/dubbo-demo/dubbo-demo-triple/pom.xml +++ b/dubbo-demo/dubbo-demo-triple/pom.xml @@ -34,7 +34,7 @@ true 1.8 1.8 - 3.21.12 + 3.22.0 3.10.1 diff --git a/dubbo-rpc/dubbo-rpc-triple/pom.xml b/dubbo-rpc/dubbo-rpc-triple/pom.xml index 7e6cf1cfdcf..4862700ff3f 100644 --- a/dubbo-rpc/dubbo-rpc-triple/pom.xml +++ b/dubbo-rpc/dubbo-rpc-triple/pom.xml @@ -32,7 +32,7 @@ 0.0.4.1-SNAPSHOT 1.0.4 3.5.3 - 3.21.12 + 3.22.0 diff --git a/dubbo-xds/pom.xml b/dubbo-xds/pom.xml index 6279ea2294e..e96257feff0 100644 --- a/dubbo-xds/pom.xml +++ b/dubbo-xds/pom.xml @@ -31,7 +31,7 @@ The xDS Integration false - 3.21.12 + 3.22.0 1.41.0 From 2897c856741ed7d642de170e6fca782eea92dc5e Mon Sep 17 00:00:00 2001 From: Mengyang Tang Date: Wed, 22 Feb 2023 20:26:39 +0800 Subject: [PATCH 11/12] Enhance the way to get dubbo version (#11574) * Enhance the way to get dubbo version * Catch Throwable * Reimplemented using antrun * Enable failOnError --- dubbo-common/pom.xml | 34 +++++++++++++++++-- .../java/org/apache/dubbo/common/Version.java | 28 +++++++++++++-- .../src/main/resources/META-INF/version | 1 + .../dubbo/common/version/VersionTest.java | 12 ++++++- .../src/test/resources/META-INF/version | 3 ++ pom.xml | 6 ++++ 6 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 dubbo-common/src/main/resources/META-INF/version create mode 100644 dubbo-common/src/test/resources/META-INF/version diff --git a/dubbo-common/pom.xml b/dubbo-common/pom.xml index 3205760a5d3..335eee3002b 100644 --- a/dubbo-common/pom.xml +++ b/dubbo-common/pom.xml @@ -100,8 +100,38 @@ javax.annotation javax.annotation-api - - + + + + org.apache.maven.plugins + maven-antrun-plugin + + + get-version-infos + compile + + true + + + + + + + + + + + + + run + + + + + + diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java b/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java index 9f9a1d1e11b..6b5bb4f279d 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java @@ -19,15 +19,19 @@ import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ClassUtils; +import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.StringUtils; import java.io.IOException; import java.net.URL; import java.security.CodeSource; +import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Optional; +import java.util.Properties; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -46,8 +50,9 @@ public final class Version { public static final String DEFAULT_DUBBO_PROTOCOL_VERSION = "2.0.2"; // version 1.0.0 represents Dubbo rpc protocol before v2.6.2 public static final int LEGACY_DUBBO_PROTOCOL_VERSION = 10000; // 1.0.0 - // Dubbo implementation version, usually is jar version. - private static final String VERSION = getVersion(Version.class, ""); + // Dubbo implementation version. + private static String VERSION; + private static String LATEST_COMMIT_ID; /** * For protocol compatibility purpose. @@ -61,6 +66,21 @@ public final class Version { static { // check if there's duplicated jar Version.checkDuplicate(Version.class); + + // get dubbo version and last commit id + try { + Properties properties = + ConfigUtils.loadProperties(Collections.emptySet(), "META-INF/version"); + + VERSION = Optional.ofNullable(properties.getProperty("revision")) + .filter(StringUtils::isNotBlank) + .orElseGet(() -> getVersion(Version.class, "")); + LATEST_COMMIT_ID = Optional.ofNullable(properties.getProperty("git.commit.id")).orElse(""); + } catch (Throwable e) { + logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", "continue the old logic, ignore exception " + e.getMessage(), e); + VERSION = getVersion(Version.class, ""); + LATEST_COMMIT_ID = ""; + } } private Version() { @@ -74,6 +94,10 @@ public static String getVersion() { return VERSION; } + public static String getLastCommitId() { + return LATEST_COMMIT_ID; + } + /** * Compare versions * diff --git a/dubbo-common/src/main/resources/META-INF/version b/dubbo-common/src/main/resources/META-INF/version new file mode 100644 index 00000000000..c53621b1abc --- /dev/null +++ b/dubbo-common/src/main/resources/META-INF/version @@ -0,0 +1 @@ +# This is a placeholder file, the real file will be output when the project compiles diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/version/VersionTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/version/VersionTest.java index 39093a8d555..0c6604e74d9 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/version/VersionTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/version/VersionTest.java @@ -91,4 +91,14 @@ void testIsFramework263OrHigher() { Assertions.assertTrue(Version.isRelease263OrHigher("2.6.3")); Assertions.assertTrue(Version.isRelease263OrHigher("2.6.3.0")); } -} \ No newline at end of file + + @Test + void testGetVersion() { + Assertions.assertEquals("1.0.0", Version.getVersion()); + } + + @Test + void testGetLastCommitId() { + Assertions.assertEquals("82a29fcd674216fe9bea10b6efef3196929dd7ca", Version.getLastCommitId()); + } +} diff --git a/dubbo-common/src/test/resources/META-INF/version b/dubbo-common/src/test/resources/META-INF/version new file mode 100644 index 00000000000..2fc6840bf04 --- /dev/null +++ b/dubbo-common/src/test/resources/META-INF/version @@ -0,0 +1,3 @@ +# This is a test file +revision=1.0.0 +git.commit.id=82a29fcd674216fe9bea10b6efef3196929dd7ca diff --git a/pom.xml b/pom.xml index be793127bb0..9323273df3d 100644 --- a/pom.xml +++ b/pom.xml @@ -124,6 +124,7 @@ 0.8.8 1.3.0 3.2.1 + 3.1.0 true true @@ -801,6 +802,11 @@ jetty-maven-plugin ${maven_jetty_version} + + org.apache.maven.plugins + maven-antrun-plugin + ${maven_antrun_version} + From 671e0f8bb9b7ebb447314660eea062869283888f Mon Sep 17 00:00:00 2001 From: wxbty <38374721+wxbty@users.noreply.github.com> Date: Wed, 22 Feb 2023 20:44:58 +0800 Subject: [PATCH 12/12] Observability task: metadata center (#11593) * init metadata * add pom * add licence * add licence * remove unuse pom * remove unuse pom * remove unuse pom * fix test * fix test * fix test * fix pom * use applicationModel * remove unuse * add test * add push testcase * add test case * add test case * add testcase * rename * opt * debug * fix testcase * add pom --------- Co-authored-by: x-shadow-man <1494445739@qq.com> --- .../apache/dubbo/config/MetricsConfig.java | 19 +- dubbo-config/dubbo-config-api/pom.xml | 6 + .../deploy/DefaultApplicationDeployer.java | 2 +- dubbo-distribution/dubbo-all/pom.xml | 8 + dubbo-distribution/dubbo-bom/pom.xml | 5 + .../ApplicationMetricsCollector.java | 8 +- .../metrics/collector/MetricsCollector.java | 9 +- .../event/GlobalMetricsEventMulticaster.java | 12 +- .../metrics/listener/MetricsLifeListener.java | 6 +- .../metrics/listener/MetricsListener.java | 3 +- .../dubbo/metrics/model/MetricsKey.java | 26 ++- .../metrics/model/MetricsKeyWrapper.java | 4 +- .../SimpleMetricsEventMulticasterTest.java | 8 +- .../metrics/MetricsScopeModelInitializer.java | 2 +- dubbo-metrics/dubbo-metrics-metadata/pom.xml | 40 ++++ .../collector/MetadataMetricsCollector.java | 111 +++++++++ .../collector/stat/MetadataStatComposite.java | 212 ++++++++++++++++++ .../metrics/metadata/event/MetadataEvent.java | 109 +++++++++ .../MetadataMetricsEventMulticaster.java | 31 +++ .../metadata/event/MetricsPushListener.java | 49 ++++ .../event/MetricsSubscribeListener.java | 49 ++++ ...e.dubbo.metrics.collector.MetricsCollector | 1 + .../MetadataMetricsCollectorTest.java | 168 ++++++++++++++ dubbo-metrics/pom.xml | 1 + .../META-INF/native-image/reflect-config.json | 12 +- dubbo-registry/dubbo-registry-api/pom.xml | 18 ++ .../client/AbstractServiceDiscovery.java | 39 +++- dubbo-test/dubbo-dependencies-all/pom.xml | 4 + 28 files changed, 919 insertions(+), 43 deletions(-) create mode 100644 dubbo-metrics/dubbo-metrics-metadata/pom.xml create mode 100644 dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/collector/MetadataMetricsCollector.java create mode 100644 dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/collector/stat/MetadataStatComposite.java create mode 100644 dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataEvent.java create mode 100644 dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataMetricsEventMulticaster.java create mode 100644 dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetricsPushListener.java create mode 100644 dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetricsSubscribeListener.java create mode 100644 dubbo-metrics/dubbo-metrics-metadata/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector create mode 100644 dubbo-metrics/dubbo-metrics-metadata/src/test/java/metrics/metrics/collector/MetadataMetricsCollectorTest.java diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java index 8811d5d612f..1b8340138b2 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java @@ -16,9 +16,6 @@ */ package org.apache.dubbo.config; -import java.util.HashMap; -import java.util.Map; - import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.config.nested.AggregationConfig; @@ -26,6 +23,9 @@ import org.apache.dubbo.config.support.Nested; import org.apache.dubbo.rpc.model.ApplicationModel; +import java.util.HashMap; +import java.util.Map; + /** * MetricsConfig */ @@ -40,6 +40,11 @@ public class MetricsConfig extends AbstractConfig { */ private Boolean enableJvmMetrics; + /** + * Enable jvm metrics when collecting. + */ + private Boolean enableMetadataMetrics; + /** * @deprecated After metrics config is refactored. * This parameter should no longer use and will be deleted in the future. @@ -137,5 +142,13 @@ public Integer getExportServicePort() { public void setExportServicePort(Integer exportServicePort) { this.exportServicePort = exportServicePort; } + + public Boolean getEnableMetadataMetrics() { + return enableMetadataMetrics; + } + + public void setEnableMetadataMetrics(Boolean enableMetadataMetrics) { + this.enableMetadataMetrics = enableMetadataMetrics; + } } diff --git a/dubbo-config/dubbo-config-api/pom.xml b/dubbo-config/dubbo-config-api/pom.xml index c863389111f..2ba42d26a38 100644 --- a/dubbo-config/dubbo-config-api/pom.xml +++ b/dubbo-config/dubbo-config-api/pom.xml @@ -48,6 +48,12 @@ ${project.parent.version} + + org.apache.dubbo + dubbo-metrics-metadata + ${project.parent.version} + + org.apache.dubbo dubbo-monitor-api diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java index e5853a9f4cd..c526864ab4e 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java @@ -359,7 +359,7 @@ private void initMetricsService() { private void initMetricsReporter() { DefaultMetricsCollector collector = - applicationModel.getFrameworkModel().getBeanFactory().getBean(DefaultMetricsCollector.class); + applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class); MetricsConfig metricsConfig = configManager.getMetrics().orElse(null); // TODO compatible with old usage of metrics, remove protocol check after new metrics is ready for use. if (metricsConfig != null && PROTOCOL_PROMETHEUS.equals(metricsConfig.getProtocol())) { diff --git a/dubbo-distribution/dubbo-all/pom.xml b/dubbo-distribution/dubbo-all/pom.xml index 23b1192686c..7e966591525 100644 --- a/dubbo-distribution/dubbo-all/pom.xml +++ b/dubbo-distribution/dubbo-all/pom.xml @@ -197,6 +197,13 @@ compile true + + org.apache.dubbo + dubbo-metrics-metadata + ${project.version} + compile + true + @@ -495,6 +502,7 @@ org.apache.dubbo:dubbo-metadata-report-zookeeper org.apache.dubbo:dubbo-metrics-api org.apache.dubbo:dubbo-metrics-default + org.apache.dubbo:dubbo-metrics-metadata org.apache.dubbo:dubbo-metrics-prometheus org.apache.dubbo:dubbo-monitor-api org.apache.dubbo:dubbo-monitor-default diff --git a/dubbo-distribution/dubbo-bom/pom.xml b/dubbo-distribution/dubbo-bom/pom.xml index 4b22ec55f94..42b26038cd6 100644 --- a/dubbo-distribution/dubbo-bom/pom.xml +++ b/dubbo-distribution/dubbo-bom/pom.xml @@ -240,6 +240,11 @@ dubbo-metrics-prometheus ${project.version} + + org.apache.dubbo + dubbo-metrics-metadata + ${project.version} + diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/ApplicationMetricsCollector.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/ApplicationMetricsCollector.java index b437561a7bf..c1dc63e5b27 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/ApplicationMetricsCollector.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/ApplicationMetricsCollector.java @@ -17,18 +17,18 @@ package org.apache.dubbo.metrics.collector; +import org.apache.dubbo.metrics.event.MetricsEvent; + /** * Application-level collector. * registration center, configuration center and other scenarios * * @Params metrics type */ -public interface ApplicationMetricsCollector extends MetricsCollector { +public interface ApplicationMetricsCollector extends MetricsCollector { void increment(String applicationName, T type); - void decrease(String applicationName, T type); - - void addRT(String applicationName, Long responseTime); + void addRT(String applicationName, String registryOpType, Long responseTime); } diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/MetricsCollector.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/MetricsCollector.java index d722f775478..3436d4f8298 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/MetricsCollector.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/MetricsCollector.java @@ -19,7 +19,7 @@ import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.metrics.event.MetricsEvent; -import org.apache.dubbo.metrics.listener.MetricsListener; +import org.apache.dubbo.metrics.listener.MetricsLifeListener; import org.apache.dubbo.metrics.model.sample.MetricSample; import java.util.List; @@ -29,11 +29,12 @@ * An interface of collector to collect framework internal metrics. */ @SPI -public interface MetricsCollector extends MetricsListener { +public interface MetricsCollector extends MetricsLifeListener { default boolean isCollectEnabled() { return false; } + /** * Collect metrics as {@link MetricSample} * @@ -41,8 +42,4 @@ default boolean isCollectEnabled() { */ List collect(); - @Override - default void onEvent(MetricsEvent event) { - - } } diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/GlobalMetricsEventMulticaster.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/GlobalMetricsEventMulticaster.java index b5d4effdfeb..28f01d5c276 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/GlobalMetricsEventMulticaster.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/GlobalMetricsEventMulticaster.java @@ -20,15 +20,19 @@ import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.metrics.collector.MetricsCollector; -import org.apache.dubbo.rpc.model.FrameworkModel; +import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.List; +/** + * Global spi event publisher + */ public class GlobalMetricsEventMulticaster extends SimpleMetricsEventMulticaster { - public GlobalMetricsEventMulticaster(FrameworkModel frameworkModel) { - ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory(); - ExtensionLoader extensionLoader = frameworkModel.getExtensionLoader(MetricsCollector.class); + @SuppressWarnings({"rawtypes"}) + public GlobalMetricsEventMulticaster(ApplicationModel applicationModel) { + ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); + ExtensionLoader extensionLoader = applicationModel.getExtensionLoader(MetricsCollector.class); if (extensionLoader != null) { List customizeCollectors = extensionLoader .getActivateExtensions(); diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsLifeListener.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsLifeListener.java index 80098f67bf1..bee82056be7 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsLifeListener.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsLifeListener.java @@ -24,7 +24,9 @@ */ public interface MetricsLifeListener extends MetricsListener { - void onEventFinish(E event); + default void onEventFinish(E event) { + } - void onEventError(E event); + default void onEventError(E event) { + } } diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsListener.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsListener.java index 058f583e94a..64b4796af37 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsListener.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsListener.java @@ -33,6 +33,7 @@ default boolean isSupport(MetricsEvent event) { * * @param event BaseMetricsEvent */ - void onEvent(E event); + default void onEvent(E event) { + } } diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKey.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKey.java index 7649af788aa..96c879c8dd7 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKey.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKey.java @@ -30,7 +30,6 @@ public enum MetricsKey { METRIC_REQUESTS_FAILED("dubbo.%s.requests.unknown.failed.total", "Unknown Failed Requests"), METRIC_REQUESTS_TOTAL_FAILED("dubbo.%s.requests.failed.total", "Total Failed Requests"), - METRIC_REQUESTS_TOTAL_AGG("dubbo.%s.requests.total.aggregate", "Aggregated Total Requests"), METRIC_REQUESTS_SUCCEED_AGG("dubbo.%s.requests.succeed.aggregate", "Aggregated Succeed Requests"), METRIC_REQUESTS_FAILED_AGG("dubbo.%s.requests.failed.aggregate", "Aggregated Failed Requests"), @@ -60,12 +59,25 @@ public enum MetricsKey { GENERIC_METRIC_RT_P99("dubbo.%s.rt.seconds.p99", "Response Time P99"), GENERIC_METRIC_RT_P95("dubbo.%s.rt.seconds.p95", "Response Time P95"), - THREAD_POOL_CORE_SIZE("dubbo.thread.pool.core.size","Thread Pool Core Size"), - THREAD_POOL_LARGEST_SIZE("dubbo.thread.pool.largest.size","Thread Pool Largest Size"), - THREAD_POOL_MAX_SIZE("dubbo.thread.pool.max.size","Thread Pool Max Size"), - THREAD_POOL_ACTIVE_SIZE("dubbo.thread.pool.active.size","Thread Pool Active Size"), - THREAD_POOL_THREAD_COUNT("dubbo.thread.pool.thread.count","Thread Pool Thread Count"), - THREAD_POOL_QUEUE_SIZE("dubbo.thread.pool.queue.size","Thread Pool Queue Size"); + THREAD_POOL_CORE_SIZE("dubbo.thread.pool.core.size", "Thread Pool Core Size"), + THREAD_POOL_LARGEST_SIZE("dubbo.thread.pool.largest.size", "Thread Pool Largest Size"), + THREAD_POOL_MAX_SIZE("dubbo.thread.pool.max.size", "Thread Pool Max Size"), + THREAD_POOL_ACTIVE_SIZE("dubbo.thread.pool.active.size", "Thread Pool Active Size"), + THREAD_POOL_THREAD_COUNT("dubbo.thread.pool.thread.count", "Thread Pool Thread Count"), + THREAD_POOL_QUEUE_SIZE("dubbo.thread.pool.queue.size", "Thread Pool Queue Size"), + + // metadata push metrics key + METADATA_PUSH_METRIC_NUM("dubbo.metadata.push.num.total", "Total Push Num"), + METADATA_PUSH_METRIC_NUM_SUCCEED("dubbo.metadata.push.num.succeed.total", "Succeed Push Num"), + METADATA_PUSH_METRIC_NUM_FAILED("dubbo.metadata.push.num.failed.total", "Failed Push Num"), + + // metadata subscribe metrics key + METADATA_SUBSCRIBE_METRIC_NUM("dubbo.metadata.subscribe.num.total", "Total Metadata Subscribe Num"), + METADATA_SUBSCRIBE_METRIC_NUM_SUCCEED("dubbo.metadata.subscribe.num.succeed.total", "Succeed Metadata Subscribe Num"), + METADATA_SUBSCRIBE_METRIC_NUM_FAILED("dubbo.metadata.subscribe.num.failed.total", "Failed Metadata Subscribe Num"), + + // consumer metrics key + ; private final String name; private final String description; diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKeyWrapper.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKeyWrapper.java index 0a0afc95e8d..54b791e5a89 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKeyWrapper.java +++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKeyWrapper.java @@ -38,8 +38,8 @@ public MetricsKey getMetricsKey() { return metricsKey; } - public boolean isKey(MetricsKey metricsKey) { - return metricsKey == getMetricsKey(); + public boolean isKey(MetricsKey metricsKey, String registryOpType) { + return metricsKey == getMetricsKey() && registryOpType.equals(getType()); } public String targetKey() { diff --git a/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticasterTest.java b/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticasterTest.java index de5314d803e..15f6e66f23d 100644 --- a/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticasterTest.java +++ b/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticasterTest.java @@ -18,6 +18,7 @@ package org.apache.dubbo.metrics.event; import org.apache.dubbo.metrics.listener.MetricsLifeListener; +import org.apache.dubbo.metrics.listener.MetricsListener; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -32,7 +33,12 @@ public class SimpleMetricsEventMulticasterTest { public void setup() { eventMulticaster = new SimpleMetricsEventMulticaster(); obj = new Object[]{new Object()}; - eventMulticaster.addListener(event -> obj[0] = new Object()); + eventMulticaster.addListener(new MetricsListener() { + @Override + public void onEvent(MetricsEvent event) { + obj[0] = new Object(); + } + }); requestEvent = new RequestEvent(obj[0], MetricsEvent.Type.TOTAL); } diff --git a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/MetricsScopeModelInitializer.java b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/MetricsScopeModelInitializer.java index 6f786b2c678..f8236f60d2b 100644 --- a/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/MetricsScopeModelInitializer.java +++ b/dubbo-metrics/dubbo-metrics-default/src/main/java/org/apache/dubbo/metrics/MetricsScopeModelInitializer.java @@ -34,7 +34,7 @@ public void initializeFrameworkModel(FrameworkModel frameworkModel) { @Override public void initializeApplicationModel(ApplicationModel applicationModel) { - ScopeBeanFactory beanFactory = applicationModel.getFrameworkModel().getBeanFactory(); + ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); beanFactory.registerBean(DefaultMetricsCollector.class); beanFactory.registerBean(GlobalMetricsEventMulticaster.class); } diff --git a/dubbo-metrics/dubbo-metrics-metadata/pom.xml b/dubbo-metrics/dubbo-metrics-metadata/pom.xml new file mode 100644 index 00000000000..f454e9558ed --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-metadata/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + org.apache.dubbo + dubbo-metrics + ${revision} + ../pom.xml + + dubbo-metrics-metadata + jar + ${project.artifactId} + The metrics module of dubbo project + + false + + + + org.apache.dubbo + dubbo-metrics-api + ${project.parent.version} + + + diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/collector/MetadataMetricsCollector.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/collector/MetadataMetricsCollector.java new file mode 100644 index 00000000000..c9140a288ca --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/collector/MetadataMetricsCollector.java @@ -0,0 +1,111 @@ +/* + * 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.metrics.metadata.collector; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.config.context.ConfigManager; +import org.apache.dubbo.metrics.collector.ApplicationMetricsCollector; +import org.apache.dubbo.metrics.collector.MetricsCollector; +import org.apache.dubbo.metrics.event.MetricsEvent; +import org.apache.dubbo.metrics.event.MetricsEventMulticaster; +import org.apache.dubbo.metrics.metadata.collector.stat.MetadataStatComposite; +import org.apache.dubbo.metrics.metadata.event.MetadataEvent; +import org.apache.dubbo.metrics.metadata.event.MetadataMetricsEventMulticaster; +import org.apache.dubbo.metrics.model.sample.MetricSample; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + + +/** + * Registry implementation of {@link MetricsCollector} + */ +@Activate +public class MetadataMetricsCollector implements ApplicationMetricsCollector { + + private Boolean collectEnabled = null; + private final MetadataStatComposite stats; + private final MetricsEventMulticaster metadataEventMulticaster; + private final ApplicationModel applicationModel; + + public MetadataMetricsCollector(ApplicationModel applicationModel) { + this.stats = new MetadataStatComposite(); + this.metadataEventMulticaster = new MetadataMetricsEventMulticaster(); + this.applicationModel = applicationModel; + } + + public void setCollectEnabled(Boolean collectEnabled) { + if (collectEnabled != null) { + this.collectEnabled = collectEnabled; + } + } + + @Override + public boolean isCollectEnabled() { + if (collectEnabled == null) { + ConfigManager configManager = applicationModel.getApplicationConfigManager(); + configManager.getMetrics().ifPresent(metricsConfig -> setCollectEnabled(metricsConfig.getEnableMetadataMetrics())); + } + return Optional.ofNullable(collectEnabled).orElse(false); + } + + @Override + public void increment(String applicationName, MetadataEvent.Type registryType) { + this.stats.increment(registryType, applicationName); + } + + @Override + public void addRT(String applicationName, String registryOpType, Long responseTime) { + stats.calcRt(applicationName, registryOpType, responseTime); + } + + @Override + public List collect() { + List list = new ArrayList<>(); + if (!isCollectEnabled()) { + return list; + } + list.addAll(stats.exportNumMetrics()); + list.addAll(stats.exportRtMetrics()); + + return list; + } + + @Override + public boolean isSupport(MetricsEvent event) { + return event instanceof MetadataEvent; + } + + @Override + public void onEvent(MetadataEvent event) { + metadataEventMulticaster.publishEvent(event); + } + + + @Override + public void onEventFinish(MetadataEvent event) { + metadataEventMulticaster.publishFinishEvent(event); + } + + @Override + public void onEventError(MetadataEvent event) { + metadataEventMulticaster.publishErrorEvent(event); + } +} diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/collector/stat/MetadataStatComposite.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/collector/stat/MetadataStatComposite.java new file mode 100644 index 00000000000..83cdbb855c8 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/collector/stat/MetadataStatComposite.java @@ -0,0 +1,212 @@ +/* + * 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.metrics.metadata.collector.stat; + +import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; +import org.apache.dubbo.metrics.collector.MetricsCollector; +import org.apache.dubbo.metrics.model.ApplicationMetric; +import org.apache.dubbo.metrics.model.MetricsCategory; +import org.apache.dubbo.metrics.model.MetricsKey; +import org.apache.dubbo.metrics.model.MetricsKeyWrapper; +import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; +import org.apache.dubbo.metrics.metadata.event.MetadataEvent; +import org.apache.dubbo.metrics.report.MetricsExport; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.LongAccumulator; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * As a data aggregator, use internal data containers calculates and classifies + * the registry data collected by {@link MetricsCollector MetricsCollector}, and + * provides an {@link MetricsExport MetricsExport} interface for exporting standard output formats. + */ +public class MetadataStatComposite implements MetricsExport { + + + public Map> numStats = new ConcurrentHashMap<>(); + public List> rtStats = new ArrayList<>(); + public static String OP_TYPE_PUSH = "push"; + public static String OP_TYPE_SUBSCRIBE = "subscribe"; + + public MetadataStatComposite() { + for (MetadataEvent.Type type : MetadataEvent.Type.values()) { + numStats.put(type, new ConcurrentHashMap<>()); + } + + rtStats.addAll(initStats(OP_TYPE_PUSH)); + rtStats.addAll(initStats(OP_TYPE_SUBSCRIBE)); + } + + private List> initStats(String registryOpType) { + List> singleRtStats = new ArrayList<>(); + singleRtStats.add(new AtomicLongContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_LAST))); + singleRtStats.add(new LongAccumulatorContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_MIN), new LongAccumulator(Long::min, Long.MAX_VALUE))); + singleRtStats.add(new LongAccumulatorContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_MAX), new LongAccumulator(Long::max, Long.MIN_VALUE))); + singleRtStats.add(new AtomicLongContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_SUM), (responseTime, longAccumulator) -> longAccumulator.addAndGet(responseTime))); + // AvgContainer is a special counter that stores the number of times but outputs function of sum/times + AtomicLongContainer avgContainer = new AtomicLongContainer(new MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_AVG), (k, v) -> v.incrementAndGet()); + avgContainer.setValueSupplier(applicationName -> { + LongContainer totalContainer = rtStats.stream().filter(longContainer -> longContainer.isKeyWrapper(MetricsKey.GENERIC_METRIC_RT_SUM, registryOpType)).findFirst().get(); + AtomicLong totalRtTimes = avgContainer.get(applicationName); + AtomicLong totalRtSum = (AtomicLong) totalContainer.get(applicationName); + return totalRtSum.get() / totalRtTimes.get(); + }); + singleRtStats.add(avgContainer); + return singleRtStats; + } + + public void increment(MetadataEvent.Type type, String applicationName) { + if (!numStats.containsKey(type)) { + return; + } + numStats.get(type).computeIfAbsent(applicationName, k -> new AtomicLong(0L)).incrementAndGet(); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public void calcRt(String applicationName, String registryOpType, Long responseTime) { + for (LongContainer container : rtStats.stream().filter(longContainer -> longContainer.specifyType(registryOpType)).collect(Collectors.toList())) { + Number current = (Number) ConcurrentHashMapUtils.computeIfAbsent(container, applicationName, container.getInitFunc()); + container.getConsumerFunc().accept(responseTime, current); + } + } + + @Override + public List exportNumMetrics() { + List list = new ArrayList<>(); + for (MetadataEvent.Type type : numStats.keySet()) { + Map stringAtomicLongMap = numStats.get(type); + for (String applicationName : stringAtomicLongMap.keySet()) { + list.add(convertToSample(applicationName, type, MetricsCategory.REGISTRY, stringAtomicLongMap.get(applicationName))); + } + } + return list; + } + + @Override + public List exportRtMetrics() { + List list = new ArrayList<>(); + for (LongContainer rtContainer : rtStats) { + MetricsKeyWrapper metricsKeyWrapper = rtContainer.getMetricsKeyWrapper(); + for (Map.Entry entry : rtContainer.entrySet()) { + list.add(new GaugeMetricSample(metricsKeyWrapper.targetKey(), metricsKeyWrapper.targetDesc(), ApplicationMetric.getTagsByName(entry.getKey()), MetricsCategory.RT, () -> rtContainer.getValueSupplier().apply(entry.getKey()))); + } + } + return list; + } + + public GaugeMetricSample convertToSample(String applicationName, MetadataEvent.Type type, MetricsCategory category, AtomicLong targetNumber) { + return new GaugeMetricSample(type.getMetricsKey(), ApplicationMetric.getTagsByName(applicationName), category, targetNumber::get); + } + + + /** + * Collect Number type data + * + * @param + */ + public static class LongContainer extends ConcurrentHashMap { + + /** + * Provide the metric type name + */ + private final MetricsKeyWrapper metricsKeyWrapper; + /** + * The initial value corresponding to the key is generally 0 of different data types + */ + private final Function initFunc; + /** + * Statistical data calculation function, which can be self-increment, self-decrement, or more complex avg function + */ + private final BiConsumer consumerFunc; + /** + * Data output function required by {@link GaugeMetricSample GaugeMetricSample} + */ + private Function valueSupplier; + + + public LongContainer(MetricsKeyWrapper metricsKeyWrapper, Supplier initFunc, BiConsumer consumerFunc) { + this.metricsKeyWrapper = metricsKeyWrapper; + this.initFunc = s -> initFunc.get(); + this.consumerFunc = consumerFunc; + this.valueSupplier = k -> this.get(k).longValue(); + } + + public boolean specifyType(String type) { + return type.equals(getMetricsKeyWrapper().getType()); + } + + public MetricsKeyWrapper getMetricsKeyWrapper() { + return metricsKeyWrapper; + } + + public boolean isKeyWrapper(MetricsKey metricsKey, String registryOpType) { + return metricsKeyWrapper.isKey(metricsKey,registryOpType); + } + + public Function getInitFunc() { + return initFunc; + } + + public BiConsumer getConsumerFunc() { + return consumerFunc; + } + + public Function getValueSupplier() { + return valueSupplier; + } + + public void setValueSupplier(Function valueSupplier) { + this.valueSupplier = valueSupplier; + } + + @Override + public String toString() { + return "LongContainer{" + + "metricsKeyWrapper=" + metricsKeyWrapper + + '}'; + } + } + + public static class AtomicLongContainer extends LongContainer { + + public AtomicLongContainer(MetricsKeyWrapper metricsKeyWrapper) { + super(metricsKeyWrapper, AtomicLong::new, (responseTime, longAccumulator) -> longAccumulator.set(responseTime)); + } + + public AtomicLongContainer(MetricsKeyWrapper metricsKeyWrapper, BiConsumer consumerFunc) { + super(metricsKeyWrapper, AtomicLong::new, consumerFunc); + } + + } + + public static class LongAccumulatorContainer extends LongContainer { + public LongAccumulatorContainer(MetricsKeyWrapper metricsKeyWrapper, LongAccumulator accumulator) { + super(metricsKeyWrapper, () -> accumulator, (responseTime, longAccumulator) -> longAccumulator.accumulate(responseTime)); + } + } + + +} diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataEvent.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataEvent.java new file mode 100644 index 00000000000..8d68e5a1892 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataEvent.java @@ -0,0 +1,109 @@ +/* + * 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.metrics.metadata.event; + +import org.apache.dubbo.metrics.event.MetricsEvent; +import org.apache.dubbo.metrics.event.TimeCounter; +import org.apache.dubbo.metrics.metadata.collector.MetadataMetricsCollector; +import org.apache.dubbo.metrics.model.MetricsKey; +import org.apache.dubbo.metrics.model.TimePair; +import org.apache.dubbo.rpc.model.ApplicationModel; + +/** + * Registry related events + */ +public class MetadataEvent extends MetricsEvent implements TimeCounter { + private final TimePair timePair; + private final MetadataMetricsCollector collector; + private final boolean available; + + public MetadataEvent(ApplicationModel applicationModel, TimePair timePair) { + super(applicationModel); + this.timePair = timePair; + this.collector = applicationModel.getBeanFactory().getBean(MetadataMetricsCollector.class); + this.available = this.collector != null && collector.isCollectEnabled(); + } + + public ApplicationModel getSource() { + return (ApplicationModel) source; + } + + public MetadataMetricsCollector getCollector() { + return collector; + } + + public boolean isAvailable() { + return available; + } + + @Override + public TimePair getTimePair() { + return timePair; + } + + public enum Type { + P_TOTAL(MetricsKey.METADATA_PUSH_METRIC_NUM), + P_SUCCEED(MetricsKey.METADATA_PUSH_METRIC_NUM_SUCCEED), + P_FAILED(MetricsKey.METADATA_PUSH_METRIC_NUM_FAILED), + + S_TOTAL(MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM), + S_SUCCEED(MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM_SUCCEED), + S_FAILED(MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM_FAILED), + + ; + + + private final MetricsKey metricsKey; + private final boolean isIncrement; + + + Type(MetricsKey metricsKey) { + this(metricsKey, true); + } + + Type(MetricsKey metricsKey, boolean isIncrement) { + this.metricsKey = metricsKey; + this.isIncrement = isIncrement; + } + + public MetricsKey getMetricsKey() { + return metricsKey; + } + + public boolean isIncrement() { + return isIncrement; + } + } + + public static class PushEvent extends MetadataEvent { + + public PushEvent(ApplicationModel applicationModel, TimePair timePair) { + super(applicationModel, timePair); + } + + } + + public static class SubscribeEvent extends MetadataEvent { + + public SubscribeEvent(ApplicationModel applicationModel, TimePair timePair) { + super(applicationModel, timePair); + } + + } + +} diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataMetricsEventMulticaster.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataMetricsEventMulticaster.java new file mode 100644 index 00000000000..92b3c96da3a --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetadataMetricsEventMulticaster.java @@ -0,0 +1,31 @@ +/* + * 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.metrics.metadata.event; + +import org.apache.dubbo.metrics.event.SimpleMetricsEventMulticaster; + +public final class MetadataMetricsEventMulticaster extends SimpleMetricsEventMulticaster { + + public MetadataMetricsEventMulticaster() { + super.addListener(new MetricsPushListener()); + super.addListener(new MetricsSubscribeListener()); + + setAvailable(); + } + +} diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetricsPushListener.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetricsPushListener.java new file mode 100644 index 00000000000..d08ec366c83 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetricsPushListener.java @@ -0,0 +1,49 @@ +/* + * 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.metrics.metadata.event; + +import org.apache.dubbo.metrics.event.MetricsEvent; +import org.apache.dubbo.metrics.listener.MetricsLifeListener; + +import static org.apache.dubbo.metrics.metadata.collector.stat.MetadataStatComposite.OP_TYPE_PUSH; + +public class MetricsPushListener implements MetricsLifeListener { + + + @Override + public boolean isSupport(MetricsEvent event) { + return event instanceof MetadataEvent.PushEvent && ((MetadataEvent) event).isAvailable(); + } + + @Override + public void onEvent(MetadataEvent.PushEvent event) { + event.getCollector().increment(event.getSource().getApplicationName(), MetadataEvent.Type.P_TOTAL); + } + + @Override + public void onEventFinish(MetadataEvent.PushEvent event) { + event.getCollector().increment(event.getSource().getApplicationName(), MetadataEvent.Type.P_SUCCEED); + event.getCollector().addRT(event.getSource().getApplicationName(), OP_TYPE_PUSH, event.getTimePair().calc()); + } + + @Override + public void onEventError(MetadataEvent.PushEvent event) { + event.getCollector().increment(event.getSource().getApplicationName(), MetadataEvent.Type.P_FAILED); + event.getCollector().addRT(event.getSource().getApplicationName(), OP_TYPE_PUSH, event.getTimePair().calc()); + } +} diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetricsSubscribeListener.java b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetricsSubscribeListener.java new file mode 100644 index 00000000000..a247f539a51 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-metadata/src/main/java/org/apache/dubbo/metrics/metadata/event/MetricsSubscribeListener.java @@ -0,0 +1,49 @@ +/* + * 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.metrics.metadata.event; + +import org.apache.dubbo.metrics.event.MetricsEvent; +import org.apache.dubbo.metrics.listener.MetricsLifeListener; + +import static org.apache.dubbo.metrics.metadata.collector.stat.MetadataStatComposite.OP_TYPE_SUBSCRIBE; + +public class MetricsSubscribeListener implements MetricsLifeListener { + + @Override + public boolean isSupport(MetricsEvent event) { + return event instanceof MetadataEvent.SubscribeEvent && ((MetadataEvent) event).isAvailable(); + } + + @Override + public void onEvent(MetadataEvent.SubscribeEvent event) { + event.getCollector().increment(event.getSource().getApplicationName(), MetadataEvent.Type.S_TOTAL); + } + + @Override + public void onEventFinish(MetadataEvent.SubscribeEvent event) { + event.getCollector().increment(event.getSource().getApplicationName(), MetadataEvent.Type.S_SUCCEED); + event.getCollector().addRT(event.getSource().getApplicationName(), OP_TYPE_SUBSCRIBE, event.getTimePair().calc()); + } + + @Override + public void onEventError(MetadataEvent.SubscribeEvent event) { + event.getCollector().increment(event.getSource().getApplicationName(), MetadataEvent.Type.S_FAILED); + event.getCollector().addRT(event.getSource().getApplicationName(), OP_TYPE_SUBSCRIBE, event.getTimePair().calc()); + } + +} diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector b/dubbo-metrics/dubbo-metrics-metadata/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector new file mode 100644 index 00000000000..c90627a2d0b --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-metadata/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector @@ -0,0 +1 @@ +metadata-collector=org.apache.dubbo.metrics.metadata.collector.MetadataMetricsCollector diff --git a/dubbo-metrics/dubbo-metrics-metadata/src/test/java/metrics/metrics/collector/MetadataMetricsCollectorTest.java b/dubbo-metrics/dubbo-metrics-metadata/src/test/java/metrics/metrics/collector/MetadataMetricsCollectorTest.java new file mode 100644 index 00000000000..a07712bccd7 --- /dev/null +++ b/dubbo-metrics/dubbo-metrics-metadata/src/test/java/metrics/metrics/collector/MetadataMetricsCollectorTest.java @@ -0,0 +1,168 @@ +/* + * 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 metrics.metrics.collector; + +import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; +import org.apache.dubbo.metrics.metadata.collector.MetadataMetricsCollector; +import org.apache.dubbo.metrics.metadata.event.MetadataEvent; +import org.apache.dubbo.metrics.model.MetricsKey; +import org.apache.dubbo.metrics.model.MetricsKeyWrapper; +import org.apache.dubbo.metrics.model.TimePair; +import org.apache.dubbo.metrics.model.sample.GaugeMetricSample; +import org.apache.dubbo.metrics.model.sample.MetricSample; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.FrameworkModel; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME; +import static org.apache.dubbo.metrics.metadata.collector.stat.MetadataStatComposite.OP_TYPE_PUSH; +import static org.apache.dubbo.metrics.metadata.collector.stat.MetadataStatComposite.OP_TYPE_SUBSCRIBE; + + +class MetadataMetricsCollectorTest { + + private ApplicationModel applicationModel; + + @BeforeEach + public void setup() { + FrameworkModel frameworkModel = FrameworkModel.defaultModel(); + applicationModel = frameworkModel.newApplication(); + ApplicationConfig config = new ApplicationConfig(); + config.setName("MockMetrics"); + + applicationModel.getApplicationConfigManager().setApplication(config); + + } + + @AfterEach + public void teardown() { + applicationModel.destroy(); + } + + @Test + void testPushMetrics() throws InterruptedException { + + TimePair timePair = TimePair.start(); + GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getOrRegisterBean(GlobalMetricsEventMulticaster.class); + MetadataMetricsCollector collector = applicationModel.getBeanFactory().getOrRegisterBean(MetadataMetricsCollector.class); + collector.setCollectEnabled(true); + + eventMulticaster.publishEvent(new MetadataEvent.PushEvent(applicationModel, timePair)); + List metricSamples = collector.collect(); + + // push success +1 + Assertions.assertEquals(metricSamples.size(), 1); + Assertions.assertTrue(metricSamples.get(0) instanceof GaugeMetricSample); + Assertions.assertEquals(metricSamples.get(0).getName(), MetricsKey.METADATA_PUSH_METRIC_NUM.getName()); + + eventMulticaster.publishFinishEvent(new MetadataEvent.PushEvent(applicationModel, timePair)); + // push finish rt +1 + metricSamples = collector.collect(); + //num(total+success) + rt(5) = 7 + Assertions.assertEquals(metricSamples.size(), 7); + long c1 = timePair.calc(); + TimePair lastTimePair = TimePair.start(); + eventMulticaster.publishEvent(new MetadataEvent.PushEvent(applicationModel, lastTimePair)); + Thread.sleep(50); + // push error rt +1 + eventMulticaster.publishErrorEvent(new MetadataEvent.PushEvent(applicationModel, lastTimePair)); + long c2 = lastTimePair.calc(); + metricSamples = collector.collect(); + + // num(total+success+error) + rt(5) + Assertions.assertEquals(metricSamples.size(), 8); + + // calc rt + for (MetricSample sample : metricSamples) { + Map tags = sample.getTags(); + Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); + } + Map sampleMap = metricSamples.stream().collect(Collectors.toMap(MetricSample::getName, k -> { + Number number = ((GaugeMetricSample) k).getSupplier().get(); + return number.longValue(); + })); + + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_PUSH, MetricsKey.GENERIC_METRIC_RT_LAST).targetKey()), lastTimePair.calc()); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_PUSH, MetricsKey.GENERIC_METRIC_RT_MIN).targetKey()), Math.min(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_PUSH, MetricsKey.GENERIC_METRIC_RT_MAX).targetKey()), Math.max(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_PUSH, MetricsKey.GENERIC_METRIC_RT_AVG).targetKey()), (c1 + c2) / 2); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_PUSH, MetricsKey.GENERIC_METRIC_RT_SUM).targetKey()), c1 + c2); + } + + @Test + void testSubscribeMetrics() throws InterruptedException { + + TimePair timePair = TimePair.start(); + GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getOrRegisterBean(GlobalMetricsEventMulticaster.class); + MetadataMetricsCollector collector = applicationModel.getBeanFactory().getOrRegisterBean(MetadataMetricsCollector.class); + collector.setCollectEnabled(true); + + eventMulticaster.publishEvent(new MetadataEvent.SubscribeEvent(applicationModel, timePair)); + List metricSamples = collector.collect(); + + // push success +1 + Assertions.assertEquals(metricSamples.size(), 1); + Assertions.assertTrue(metricSamples.get(0) instanceof GaugeMetricSample); + Assertions.assertEquals(metricSamples.get(0).getName(), MetricsKey.METADATA_SUBSCRIBE_METRIC_NUM.getName()); + + eventMulticaster.publishFinishEvent(new MetadataEvent.SubscribeEvent(applicationModel, timePair)); + // push finish rt +1 + metricSamples = collector.collect(); + //num(total+success) + rt(5) = 7 + Assertions.assertEquals(metricSamples.size(), 7); + long c1 = timePair.calc(); + TimePair lastTimePair = TimePair.start(); + eventMulticaster.publishEvent(new MetadataEvent.SubscribeEvent(applicationModel, lastTimePair)); + Thread.sleep(50); + // push error rt +1 + eventMulticaster.publishErrorEvent(new MetadataEvent.SubscribeEvent(applicationModel, lastTimePair)); + long c2 = lastTimePair.calc(); + metricSamples = collector.collect(); + + // num(total+success+error) + rt(5) + Assertions.assertEquals(metricSamples.size(), 8); + + // calc rt + for (MetricSample sample : metricSamples) { + Map tags = sample.getTags(); + Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationModel.getApplicationName()); + } + Map sampleMap = metricSamples.stream().collect(Collectors.toMap(MetricSample::getName, k -> { + Number number = ((GaugeMetricSample) k).getSupplier().get(); + return number.longValue(); + })); + + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_SUBSCRIBE, MetricsKey.GENERIC_METRIC_RT_LAST).targetKey()), lastTimePair.calc()); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_SUBSCRIBE, MetricsKey.GENERIC_METRIC_RT_MIN).targetKey()), Math.min(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_SUBSCRIBE, MetricsKey.GENERIC_METRIC_RT_MAX).targetKey()), Math.max(c1, c2)); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_SUBSCRIBE, MetricsKey.GENERIC_METRIC_RT_AVG).targetKey()), (c1 + c2) / 2); + Assertions.assertEquals(sampleMap.get(new MetricsKeyWrapper(OP_TYPE_SUBSCRIBE, MetricsKey.GENERIC_METRIC_RT_SUM).targetKey()), c1 + c2); + } + + +} diff --git a/dubbo-metrics/pom.xml b/dubbo-metrics/pom.xml index c504428498b..835c2b1b44b 100644 --- a/dubbo-metrics/pom.xml +++ b/dubbo-metrics/pom.xml @@ -20,6 +20,7 @@ dubbo-metrics-api dubbo-metrics-default + dubbo-metrics-metadata dubbo-metrics-prometheus diff --git a/dubbo-native-plugin/src/main/resources/META-INF/native-image/reflect-config.json b/dubbo-native-plugin/src/main/resources/META-INF/native-image/reflect-config.json index 8ffe07af6c0..816a1aff406 100644 --- a/dubbo-native-plugin/src/main/resources/META-INF/native-image/reflect-config.json +++ b/dubbo-native-plugin/src/main/resources/META-INF/native-image/reflect-config.json @@ -2110,6 +2110,16 @@ } ] }, + { + "name": "org.apache.dubbo.metrics.MetricsScopeModelInitializer", + "allPublicMethods": true, + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, { "name": "org.apache.dubbo.rpc.cluster.ConfiguratorFactory", "allPublicMethods": true @@ -2908,7 +2918,7 @@ "methods": [ { "name": "", - "parameterTypes": ["org.apache.dubbo.rpc.model.FrameworkModel"] + "parameterTypes": ["org.apache.dubbo.rpc.model.ApplicationModel"] } ] } diff --git a/dubbo-registry/dubbo-registry-api/pom.xml b/dubbo-registry/dubbo-registry-api/pom.xml index 8cb14965440..f9c580f8c90 100644 --- a/dubbo-registry/dubbo-registry-api/pom.xml +++ b/dubbo-registry/dubbo-registry-api/pom.xml @@ -80,5 +80,23 @@ zookeeper test + + org.apache.dubbo + dubbo-metrics-api + ${project.parent.version} + compile + + + org.apache.dubbo + dubbo-metrics-default + ${project.parent.version} + compile + + + org.apache.dubbo + dubbo-metrics-metadata + ${project.parent.version} + compile + diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java index 6241fe1778b..edb974da48d 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java @@ -16,14 +16,6 @@ */ package org.apache.dubbo.registry.client; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; @@ -34,6 +26,9 @@ import org.apache.dubbo.metadata.report.MetadataReport; import org.apache.dubbo.metadata.report.MetadataReportInstance; import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier; +import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster; +import org.apache.dubbo.metrics.metadata.event.MetadataEvent; +import org.apache.dubbo.metrics.model.TimePair; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; import org.apache.dubbo.registry.client.metadata.MetadataUtils; @@ -41,6 +36,14 @@ import org.apache.dubbo.registry.client.metadata.store.MetaCacheManager; import org.apache.dubbo.rpc.model.ApplicationModel; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_INFO_CACHE_EXPIRE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_INFO_CACHE_SIZE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE; @@ -225,12 +228,19 @@ public MetadataInfo getRemoteMetadata(String revision, List ins // try to load metadata from remote. int triedTimes = 0; while (triedTimes < 3) { + + TimePair timePair = TimePair.start(); + GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class); + eventMulticaster.publishEvent(new MetadataEvent.SubscribeEvent(applicationModel, timePair)); + metadata = MetadataUtils.getRemoteMetadata(revision, instances, metadataReport); if (metadata != MetadataInfo.EMPTY) {// succeeded metadata.init(); + eventMulticaster.publishFinishEvent(new MetadataEvent.SubscribeEvent(applicationModel, timePair)); break; } else {// failed + eventMulticaster.publishErrorEvent(new MetadataEvent.SubscribeEvent(applicationModel, timePair)); if (triedTimes > 0) { if (logger.isDebugEnabled()) { logger.debug("Retry the " + triedTimes + " times to get metadata for revision=" + revision); @@ -301,7 +311,7 @@ public List lookup(URL url) { * Can be override if registry support update instance directly. *
* NOTICE: Remind to update {@link AbstractServiceDiscovery#serviceInstance}'s reference if updated - * and report metadata by {@link AbstractServiceDiscovery#reportMetadata(MetadataInfo)} + * and report metadata by {@link AbstractServiceDiscovery#reportMetadata(MetadataInfo)} * * @param oldServiceInstance origin service instance * @param newServiceInstance new service instance @@ -351,12 +361,21 @@ protected void reportMetadata(MetadataInfo metadataInfo) { if (metadataInfo == null) { return; } + TimePair timePair = TimePair.start(); + GlobalMetricsEventMulticaster eventMulticaster = applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class); + eventMulticaster.publishEvent(new MetadataEvent.PushEvent(applicationModel, timePair)); if (metadataReport != null) { SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(serviceName, metadataInfo.getRevision()); if ((DEFAULT_METADATA_STORAGE_TYPE.equals(metadataType) && metadataReport.shouldReportMetadata()) || REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) { - metadataReport.publishAppMetadata(identifier, metadataInfo); + try { + metadataReport.publishAppMetadata(identifier, metadataInfo); + } catch (IllegalStateException e) { + eventMulticaster.publishErrorEvent(new MetadataEvent.PushEvent(applicationModel, timePair)); + throw e; + } } } + eventMulticaster.publishFinishEvent(new MetadataEvent.PushEvent(applicationModel, timePair)); MetadataInfo clonedMetadataInfo = metadataInfo.clone(); metadataInfos.put(metadataInfo.getRevision(), new MetadataInfoStat(clonedMetadataInfo)); } diff --git a/dubbo-test/dubbo-dependencies-all/pom.xml b/dubbo-test/dubbo-dependencies-all/pom.xml index f97e0a69459..66f84b58f66 100644 --- a/dubbo-test/dubbo-dependencies-all/pom.xml +++ b/dubbo-test/dubbo-dependencies-all/pom.xml @@ -148,6 +148,10 @@ org.apache.dubbo dubbo-metrics-default + + org.apache.dubbo + dubbo-metrics-metadata + org.apache.dubbo dubbo-metrics-prometheus