You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by me...@apache.org on 2020/02/17 12:16:27 UTC
[dubbo] branch master updated: 2.7.6 REST Metadata (#5738)
This is an automated email from the ASF dual-hosted git repository.
mercyblitz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo.git
The following commit(s) were added to refs/heads/master by this push:
new 7b0ae12 2.7.6 REST Metadata (#5738)
7b0ae12 is described below
commit 7b0ae127a96eb508af5c2b980b20041efff01d56
Author: Mercy Ma <me...@gmail.com>
AuthorDate: Mon Feb 17 20:15:43 2020 +0800
2.7.6 REST Metadata (#5738)
* Polish /apache/dubbo#4687 : Remove the duplicated test code in dubbo-config-spring
* Polish /apache/dubbo#4674 & /apache/dubbo#4470
* Polish /apache/dubbo#5093 : Revert the previous commit
* Polish apache/dubbo#5093 : [Feature] Dubbo Services generate the metadata of REST services
* Polish apache/dubbo#5306 : [Migration] Upgrade the @since tags in Javadoc migration cloud native to master
* Polish apache/dubbo#5306 : [Migration] Upgrade the @since tags in Javadoc migration cloud native to master
* Polish apache/dubbo#5309 : [ISSURE] The beans of Dubbo's Config can't be found on the ReferenceBean's initialization
* Polish apache/dubbo#5312 : Resolve the demos' issues of zookeeper and nacos
* Polish apache/dubbo#5313 : [Migration] migrate the code in common module from cloud-native branch to master
* Polish apache/dubbo#5316 : [Refactor] Replace @EnableDubboConfigBinding Using spring-context-support
* Polish apache/dubbo#5317 : [Refactor] Refactor ReferenceAnnotationBeanPostProcessor using Alibaba spring-context-suuport API
* Polish apache/dubbo#5321 : Remove BeanFactoryUtils
* Polish apache/dubbo#5321 : Remove AnnotatedBeanDefinitionRegistryUtils
* Polish apache/dubbo#5321 : Remove AnnotationUtils
* Polish apache/dubbo#5321 : Remove ClassUtils
* Polish apache/dubbo#5321 : Remove BeanRegistrar
* Polish apache/dubbo#5321 : Remove ObjectUtils
* Polish apache/dubbo#5321 : Remove PropertySourcesUtils
* Polish apache/dubbo#5325 : [Migration] To migrate dubbo-metadata-api from cloud-native branch
* Polish apache/dubbo#5326 : [Migration] To migrate dubbo-metadata-processor from cloud-native branch
* Polish apache/dubbo#5329 : [Feature] To add the default metadata into ServiceInstance
* Polish apache/dubbo#5339 : [Refactor] Refactor the DynamicConfiguration interface
* Polish bugfix
* Fixes test cases
* Merge remote-tracking branch 'upstream/master' into cloud-native-2.7.5
# Conflicts:
# dubbo-configcenter/dubbo-configcenter-zookeeper/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java
# dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMappingTest.java
* Merge remote-tracking branch 'upstream/master' into cloud-native-2.7.5
# Conflicts:
# dubbo-configcenter/dubbo-configcenter-zookeeper/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java
# dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMappingTest.java
* Polish /apache/dubbo#5721 : [Enhancement] Setting the default IDs for Dubbo's Config Beans
* Polish /apache/dubbo#5729 : [Optimization] To remove EnableDubboConfigBinding and EnableDubboConfigBindings
* Polish /apache/dubbo#5594 : [Feature] Add the resolver of ServiceRestMetadata based on Java Reflection
* Polish /apache/dubbo#5736 : [Feature] Introducing Conversion features
* Polish /apache/dubbo#5737 : [Feature] Introducing "dubbo-metadata-processor" module
* Polish /apache/dubbo#5594 : Change the Metadata implementation
* Polish /apache/dubbo#5594 : Fixed test cases
* Polish /apache/dubbo#5594 : Fixed test cases
* Polish /apache/dubbo#5594 : Fixed test cases
* Polish /apache/dubbo#5594 : Fixed test cases
* Polish /apache/dubbo#5594 : Fixed test cases
* Polish /apache/dubbo#5594 : Fixed test cases
* Polish /apache/dubbo#5594 : Fixed test cases
* Polish /apache/dubbo#5594 : Fixed test cases
---
dubbo-all/pom.xml | 191 ++++++---
.../org/apache/dubbo/common/convert/Converter.java | 91 ++++
.../dubbo/common/convert/StringConverter.java | 27 ++
.../common/convert/StringToBooleanConverter.java | 38 ++
.../common/convert/StringToCharArrayConverter.java | 39 ++
.../common/convert/StringToCharacterConverter.java | 44 ++
.../common/convert/StringToDoubleConverter.java | 39 ++
.../common/convert/StringToFloatConverter.java | 38 ++
.../common/convert/StringToIntegerConverter.java | 38 ++
.../common/convert/StringToLongConverter.java | 39 ++
.../common/convert/StringToOptionalConverter.java | 40 ++
.../common/convert/StringToShortConverter.java | 39 ++
.../common/convert/StringToStringConverter.java | 30 ++
.../convert/multiple/MultiValueConverter.java | 64 +++
.../convert/multiple/StringToArrayConverter.java | 60 +++
.../multiple/StringToBlockingDequeConverter.java | 33 ++
.../multiple/StringToBlockingQueueConverter.java | 34 ++
.../multiple/StringToCollectionConverter.java | 33 ++
.../convert/multiple/StringToDequeConverter.java | 33 ++
.../multiple/StringToIterableConverter.java | 80 ++++
.../convert/multiple/StringToListConverter.java | 33 ++
.../multiple/StringToMultiValueConverter.java | 61 +++
.../multiple/StringToNavigableSetConverter.java | 34 ++
.../convert/multiple/StringToQueueConverter.java | 34 ++
.../convert/multiple/StringToSetConverter.java | 33 ++
.../multiple/StringToSortedSetConverter.java | 33 ++
.../multiple/StringToTransferQueueConverter.java | 33 ++
.../dubbo/common/extension/ExtensionLoader.java | 11 +-
.../apache/dubbo/common/utils/AnnotationUtils.java | 452 ++++++++++++++++++++
.../dubbo/common/utils/CharSequenceComparator.java | 37 ++
.../org/apache/dubbo/common/utils/ClassUtils.java | 226 +++++++++-
.../apache/dubbo/common/utils/CollectionUtils.java | 109 ++++-
.../org/apache/dubbo/common/utils/HttpUtils.java | 264 ++++++++++++
.../org/apache/dubbo/common/utils/MemberUtils.java | 62 +++
.../dubbo/common/utils/MethodComparator.java | 71 ++++
.../org/apache/dubbo/common/utils/MethodUtils.java | 292 ++++++++++++-
.../apache/dubbo/common/utils/ReflectUtils.java | 34 ++
.../common/utils/ServiceAnnotationResolver.java | 119 ++++++
.../org/apache/dubbo/common/utils/TypeUtils.java | 224 ++++++++++
.../org.apache.dubbo.common.convert.Converter | 11 +
...bbo.common.convert.multiple.MultiValueConverter | 12 +
.../dubbo/common/utils/AnnotationUtilsTest.java | 343 +++++++++++++++
.../apache/dubbo/common/utils/MemberUtilsTest.java | 42 ++
.../convert/StringToBooleanConverterTest.java | 58 +++
.../convert/StringToCharArrayConverterTest.java | 54 +++
.../convert/StringToCharacterConverterTest.java | 58 +++
.../dubbo/convert/StringToDoubleConverterTest.java | 58 +++
.../dubbo/convert/StringToFloatConverterTest.java | 58 +++
.../convert/StringToIntegerConverterTest.java | 58 +++
.../dubbo/convert/StringToLongConverterTest.java | 58 +++
.../convert/StringToOptionalConverterTest.java | 55 +++
.../dubbo/convert/StringToShortConverterTest.java | 58 +++
.../dubbo/convert/StringToStringConverterTest.java | 54 +++
.../multiple/StringToArrayConverterTest.java | 70 +++
.../StringToBlockingDequeConverterTest.java | 122 ++++++
.../StringToBlockingQueueConverterTest.java | 125 ++++++
.../multiple/StringToCollectionConverterTest.java | 119 ++++++
.../multiple/StringToDequeConverterTest.java | 120 ++++++
.../multiple/StringToListConverterTest.java | 119 ++++++
.../StringToNavigableSetConverterTest.java | 119 ++++++
.../multiple/StringToQueueConverterTest.java | 119 ++++++
.../convert/multiple/StringToSetConverterTest.java | 118 ++++++
.../multiple/StringToSortedSetConverterTest.java | 119 ++++++
.../StringToTransferQueueConverterTest.java | 122 ++++++
.../apache/dubbo/generic/GenericServiceTest.java | 3 +-
.../apache/dubbo/config/spring/ServiceBean.java | 3 +-
dubbo-metadata/dubbo-metadata-api/pom.xml | 14 +
.../definition/MethodDefinitionBuilder.java | 78 ++++
.../metadata/definition/TypeDefinitionBuilder.java | 4 +-
.../definition/builder/MapTypeBuilder.java | 37 +-
.../AbstractAnnotatedMethodParameterProcessor.java | 51 +++
.../rest/AbstractServiceRestMetadataResolver.java | 341 +++++++++++++++
.../rest/AnnotatedMethodParameterProcessor.java | 66 +++
.../rest/ClassPathServiceRestMetadataReader.java | 82 ++++
.../rest/DefaultServiceRestMetadataResolver.java | 59 +++
.../dubbo/metadata/rest/RequestMetadata.java | 226 ++++++++++
.../dubbo/metadata/rest/RestMetadataConstants.java | 112 +++++
.../dubbo/metadata/rest/RestMethodMetadata.java | 195 +++++++++
.../dubbo/metadata/rest/ServiceRestMetadata.java | 103 +++++
.../metadata/rest/ServiceRestMetadataReader.java | 39 ++
.../metadata/rest/ServiceRestMetadataResolver.java | 43 ++
.../rest/jaxrs/DefaultValueParameterProcessor.java | 74 ++++
.../rest/jaxrs/FormParamParameterProcessor.java | 34 ++
.../rest/jaxrs/HeaderParamParameterProcessor.java | 49 +++
.../jaxrs/JAXRSServiceRestMetadataResolver.java | 99 +++++
.../rest/jaxrs/MatrixParamParameterProcessor.java | 34 ++
.../jaxrs/ParamAnnotationParameterProcessor.java | 38 ++
.../rest/jaxrs/QueryParamParameterProcessor.java | 34 ++
...bstractRequestAnnotationParameterProcessor.java | 62 +++
.../springmvc/RequestHeaderParameterProcessor.java | 43 ++
.../springmvc/RequestParamParameterProcessor.java | 43 ++
.../SpringMvcServiceRestMetadataResolver.java | 128 ++++++
...metadata.rest.AnnotatedMethodParameterProcessor | 10 +
...dubbo.metadata.rest.ServiceRestMetadataResolver | 3 +
...Test.java => ServiceDefinitionBuilderTest.java} | 4 +-
.../dubbo/metadata/rest/DefaultRestService.java | 69 +++
.../apache/dubbo/metadata/rest/RestService.java | 42 ++
.../dubbo/metadata/rest/SpringRestService.java | 97 +++++
.../dubbo/metadata/rest/StandardRestService.java | 107 +++++
.../java/org/apache/dubbo/metadata/rest/User.java | 61 +++
.../JAXRSServiceRestMetadataResolverTest.java | 82 ++++
.../dubbo/jax-rs-service-rest-metadata.json | 349 +++++++++++++++
.../protobuf/ProtobufTypeBuilderTest.java | 4 +-
dubbo-metadata/dubbo-metadata-processor/pom.xml | 177 ++++++++
.../AbstractServiceAnnotationProcessor.java | 108 +++++
.../processing/ClassPathMetadataStorage.java | 105 +++++
...rviceDefinitionMetadataAnnotationProcessor.java | 60 +++
.../builder/ArrayTypeDefinitionBuilder.java | 50 +++
.../builder/CollectionTypeDefinitionBuilder.java | 61 +++
.../builder/DeclaredTypeDefinitionBuilder.java | 49 +++
.../builder/EnumTypeDefinitionBuilder.java | 55 +++
.../builder/GeneralTypeDefinitionBuilder.java | 65 +++
.../builder/MapTypeDefinitionBuilder.java | 62 +++
.../builder/MethodDefinitionBuilder.java | 53 +++
.../builder/PrimitiveTypeDefinitionBuilder.java | 48 +++
.../builder/ServiceDefinitionBuilder.java | 56 +++
.../builder/SimpleTypeDefinitionBuilder.java | 49 +++
.../processing/builder/TypeDefinitionBuilder.java | 96 +++++
.../AbstractAnnotatedMethodParameterProcessor.java | 52 +++
.../rest/AbstractServiceRestMetadataResolver.java | 291 +++++++++++++
.../rest/AnnotatedMethodParameterProcessor.java | 64 +++
.../rest/DefaultServiceRestMetadataResolver.java | 186 ++++++++
.../ServiceRestMetadataAnnotationProcessor.java | 86 ++++
.../rest/ServiceRestMetadataResolver.java | 55 +++
.../rest/ServiceRestMetadataStorage.java | 65 +++
.../rest/jaxrs/DefaultValueParameterProcessor.java | 75 ++++
.../rest/jaxrs/FormParamParameterProcessor.java | 34 ++
.../rest/jaxrs/HeaderParamParameterProcessor.java | 51 +++
.../jaxrs/JAXRSServiceRestMetadataResolver.java | 108 +++++
.../rest/jaxrs/MatrixParamParameterProcessor.java | 34 ++
.../jaxrs/ParamAnnotationParameterProcessor.java | 38 ++
.../rest/jaxrs/QueryParamParameterProcessor.java | 34 ++
...bstractRequestAnnotationParameterProcessor.java | 69 +++
.../springmvc/RequestHeaderParameterProcessor.java | 43 ++
.../springmvc/RequestParamParameterProcessor.java | 44 ++
.../SpringMvcServiceRestMetadataResolver.java | 165 ++++++++
.../processing/util/AnnotationUtils.java | 233 ++++++++++
.../util/ExecutableElementComparator.java | 72 ++++
.../annotation/processing/util/FieldUtils.java | 146 +++++++
.../annotation/processing/util/LoggerUtils.java | 45 ++
.../annotation/processing/util/MemberUtils.java | 94 +++++
.../annotation/processing/util/MethodUtils.java | 156 +++++++
.../processing/util/ServiceAnnotationUtils.java | 121 ++++++
.../annotation/processing/util/TypeUtils.java | 400 ++++++++++++++++++
...tation.processing.builder.TypeDefinitionBuilder | 7 +
...ocessing.rest.AnnotatedMethodParameterProcessor | 10 +
...ion.processing.rest.ServiceRestMetadataResolver | 3 +
.../services/javax.annotation.processing.Processor | 2 +
.../AbstractAnnotationProcessingTest.java | 69 +++
.../builder/ArrayTypeDefinitionBuilderTest.java | 121 ++++++
.../CollectionTypeDefinitionBuilderTest.java | 105 +++++
.../builder/EnumTypeDefinitionBuilderTest.java | 67 +++
.../builder/GeneralTypeDefinitionBuilderTest.java | 67 +++
.../builder/MapTypeDefinitionBuilderTest.java | 134 ++++++
.../PrimitiveTypeDefinitionBuilderTest.java | 130 ++++++
.../builder/ServiceDefinitionBuilderTest.java | 64 +++
.../builder/SimpleTypeDefinitionBuilderTest.java | 146 +++++++
.../processing/model/ArrayTypeModel.java | 36 ++
.../processing/model/CollectionTypeModel.java | 42 ++
.../annotation/processing/model/Color.java | 46 ++
.../annotation/processing/model/MapTypeModel.java | 41 ++
.../annotation/processing/model/Model.java | 89 ++++
.../processing/model/PrimitiveTypeModel.java | 73 ++++
.../processing/model/SimpleTypeModel.java | 161 +++++++
.../AnnotatedMethodParameterProcessorTest.java | 59 +++
.../processing/util/AnnotationUtilsTest.java | 231 ++++++++++
.../annotation/processing/util/FieldUtilsTest.java | 259 ++++++++++++
.../processing/util/LoggerUtilsTest.java | 50 +++
.../processing/util/MemberUtilsTest.java | 113 +++++
.../processing/util/MethodUtilsTest.java | 195 +++++++++
.../util/ServiceAnnotationUtilsTest.java | 136 ++++++
.../annotation/processing/util/TypeUtilsTest.java | 468 +++++++++++++++++++++
.../dubbo/metadata/rest/DefaultRestService.java | 69 +++
.../apache/dubbo/metadata/rest/RestService.java | 42 ++
.../dubbo/metadata/rest/SpringRestService.java | 97 +++++
.../dubbo/metadata/rest/StandardRestService.java | 107 +++++
.../java/org/apache/dubbo/metadata/rest/User.java | 61 +++
.../org/apache/dubbo/metadata/tools/Ancestor.java | 35 ++
.../org/apache/dubbo/metadata/tools/Compiler.java | 116 +++++
.../apache/dubbo/metadata/tools/CompilerTest.java | 37 ++
.../metadata/tools/DefaultRestServiceTest.java | 45 ++
.../dubbo/metadata/tools/DefaultTestService.java | 63 +++
.../dubbo/metadata/tools/GenericTestService.java | 38 ++
.../org/apache/dubbo/metadata/tools/Parent.java | 63 +++
.../dubbo/metadata/tools/RestServiceTest.java | 42 ++
.../metadata/tools/SpringRestServiceTest.java | 39 ++
.../metadata/tools/StandardRestServiceTest.java | 39 ++
.../apache/dubbo/metadata/tools/TestProcessor.java | 46 ++
.../apache/dubbo/metadata/tools/TestService.java | 54 +++
.../dubbo/metadata/tools/TestServiceImpl.java | 51 +++
dubbo-metadata/pom.xml | 1 +
pom.xml | 5 +-
192 files changed, 16107 insertions(+), 85 deletions(-)
diff --git a/dubbo-all/pom.xml b/dubbo-all/pom.xml
index 6904795..325083e 100644
--- a/dubbo-all/pom.xml
+++ b/dubbo-all/pom.xml
@@ -14,7 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
@@ -685,172 +686,218 @@
</artifactSet>
<transformers>
<!-- dubbo-common beginning -->
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.compiler.Compiler
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionFactory
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.infra.InfraAdapter
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.logger.LoggerAdapter
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.status.StatusChecker
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.store.DataStore
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool
</resource>
</transformer>
<!-- dubbo-common end -->
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.Dispatcher</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http.HttpBinder
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.p2p.Networker
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.InvokerListener</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.ExporterListener</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.ProxyFactory</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Cluster</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.LoadBalance
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Merger</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.ConfiguratorFactory
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.container.Container</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.monitor.MonitorFactory
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.validation.Validation</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
- <resource>META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>
+ META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory
</resource>
</transformer>
<!-- @since 2.7.5 -->
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.event.EventDispatcher
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataServiceExporter
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.metadata.WritableMetadataService
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
- <resource>META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.proxy.MetadataServiceProxyFactory
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>
+ META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.proxy.MetadataServiceProxyFactory
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
- <resource>META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>
+ META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
</resource>
</transformer>
- <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
- <resource>META-INF/dubbo/internal/org.apache.dubbo.metadata.definition.builder.TypeBuilder
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>
+ META-INF/dubbo/internal/org.apache.dubbo.metadata.definition.builder.TypeBuilder
</resource>
</transformer>
<transformer
@@ -871,6 +918,56 @@
</resource>
</transformer>
+ <!-- @since 2.7.6 -->
+
+ <!-- 'dubbo-common' module -->
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>
+ META-INF/dubbo/internal/org.apache.dubbo.common.convert.Converter
+ </resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>
+ META-INF/dubbo/internal/org.apache.dubbo.common.convert.multiple.MultiValueConverter
+ </resource>
+ </transformer>
+
+ <!-- 'dubbo-metadata-api' module -->
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>
+ META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor
+ </resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>
+ META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.ServiceRestMetadataResolver
+ </resource>
+ </transformer>
+
+ <!-- 'dubbo-metadata-processor' module -->
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>
+ META-INF/dubbo/internal/org.apache.dubbo.metadata.annotation.processing.builder.TypeDefinitionBuilder
+ </resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>
+ META-INF/dubbo/internal/org.apache.dubbo.metadata.annotation.processing.rest.AnnotatedMethodParameterProcessor
+ </resource>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>
+ META-INF/dubbo/internal/org.apache.dubbo.metadata.annotation.processing.rest.ServiceRestMetadataResolver
+ </resource>
+ </transformer>
+
</transformers>
<filters>
<filter>
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/Converter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/Converter.java
new file mode 100644
index 0000000..5bc2d4d
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/Converter.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.convert;
+
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.common.lang.Prioritized;
+
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom;
+import static org.apache.dubbo.common.utils.TypeUtils.findActualTypeArgument;
+
+/**
+ * A class to convert the source-typed value to the target-typed value
+ *
+ * @param <S> The source type
+ * @param <T> The target type
+ * @since 2.7.6
+ */
+@SPI
+@FunctionalInterface
+public interface Converter<S, T> extends Prioritized {
+
+ /**
+ * Accept the source type and target type or not
+ *
+ * @param sourceType the source type
+ * @param targetType the target type
+ * @return if accepted, return <code>true</code>, or <code>false</code>
+ */
+ default boolean accept(Class<?> sourceType, Class<?> targetType) {
+ return isAssignableFrom(sourceType, getSourceType()) && isAssignableFrom(targetType, getTargetType());
+ }
+
+ /**
+ * Convert the source-typed value to the target-typed value
+ *
+ * @param source the source-typed value
+ * @return the target-typed value
+ */
+ T convert(S source);
+
+ /**
+ * Get the source type
+ *
+ * @return non-null
+ */
+ default Class<S> getSourceType() {
+ return findActualTypeArgument(getClass(), Converter.class, 0);
+ }
+
+ /**
+ * Get the target type
+ *
+ * @return non-null
+ */
+ default Class<T> getTargetType() {
+ return findActualTypeArgument(getClass(), Converter.class, 1);
+ }
+
+ /**
+ * Get the Converter instance from {@link ExtensionLoader} with the specified source and target type
+ *
+ * @param sourceType the source type
+ * @param targetType the target type
+ * @return
+ * @see ExtensionLoader#getSupportedExtensionInstances()
+ */
+ static Converter<?, ?> getConverter(Class<?> sourceType, Class<?> targetType) {
+ return getExtensionLoader(Converter.class)
+ .getSupportedExtensionInstances()
+ .stream()
+ .filter(converter -> converter.accept(sourceType, targetType))
+ .findFirst()
+ .orElse(null);
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringConverter.java
new file mode 100644
index 0000000..9232fed
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringConverter.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.common.convert;
+
+/**
+ * A class to covert {@link String} to the target-typed value
+ *
+ * @see Converter
+ * @since 2.7.6
+ */
+@FunctionalInterface
+public interface StringConverter<T> extends Converter<String, T> {
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToBooleanConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToBooleanConverter.java
new file mode 100644
index 0000000..98655c7
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToBooleanConverter.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.common.convert;
+
+import static java.lang.Boolean.valueOf;
+import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
+
+/**
+ * The class to convert {@link String} to {@link Boolean}
+ *
+ * @since 2.7.6
+ */
+public class StringToBooleanConverter implements StringConverter<Boolean> {
+
+ @Override
+ public Boolean convert(String source) {
+ return isNotEmpty(source) ? valueOf(source) : null;
+ }
+
+ @Override
+ public int getPriority() {
+ return NORMAL_PRIORITY + 5;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToCharArrayConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToCharArrayConverter.java
new file mode 100644
index 0000000..3e40bf1
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToCharArrayConverter.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.common.convert;
+
+
+import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
+
+/**
+ * The class to convert {@link String} to <code>char[]</code>
+ *
+ * @since 2.7.6
+ */
+public class StringToCharArrayConverter implements StringConverter<char[]> {
+
+ @Override
+ public char[] convert(String source) {
+ return isNotEmpty(source) ? source.toCharArray() : null;
+ }
+
+
+ @Override
+ public int getPriority() {
+ return NORMAL_PRIORITY + 7;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToCharacterConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToCharacterConverter.java
new file mode 100644
index 0000000..580986f
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToCharacterConverter.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.common.convert;
+
+import static org.apache.dubbo.common.utils.StringUtils.length;
+
+/**
+ * The class to convert {@link String} to {@link Character}
+ *
+ * @since 2.7.6
+ */
+public class StringToCharacterConverter implements StringConverter<Character> {
+
+ @Override
+ public Character convert(String source) {
+ int length = length(source);
+ if (length == 0) {
+ return null;
+ }
+ if (length > 1) {
+ throw new IllegalArgumentException("The source String is more than one character!");
+ }
+ return source.charAt(0);
+ }
+
+ @Override
+ public int getPriority() {
+ return NORMAL_PRIORITY + 8;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToDoubleConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToDoubleConverter.java
new file mode 100644
index 0000000..148b2f8
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToDoubleConverter.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.common.convert;
+
+import static java.lang.Double.valueOf;
+import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
+
+/**
+ * The class to convert {@link String} to {@link Double}
+ *
+ * @since 2.7.6
+ */
+public class StringToDoubleConverter implements StringConverter<Double> {
+
+ @Override
+ public Double convert(String source) {
+ return isNotEmpty(source) ? valueOf(source) : null;
+ }
+
+
+ @Override
+ public int getPriority() {
+ return NORMAL_PRIORITY + 3;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToFloatConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToFloatConverter.java
new file mode 100644
index 0000000..a3b3b166
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToFloatConverter.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.common.convert;
+
+import static java.lang.Float.valueOf;
+import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
+
+/**
+ * The class to convert {@link String} to {@link Float}
+ *
+ * @since 2.7.6
+ */
+public class StringToFloatConverter implements StringConverter<Float> {
+
+ @Override
+ public Float convert(String source) {
+ return isNotEmpty(source) ? valueOf(source) : null;
+ }
+
+ @Override
+ public int getPriority() {
+ return NORMAL_PRIORITY + 4;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToIntegerConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToIntegerConverter.java
new file mode 100644
index 0000000..211f66d
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToIntegerConverter.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.common.convert;
+
+import static java.lang.Integer.valueOf;
+import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
+
+/**
+ * The class to convert {@link String} to {@link Integer}
+ *
+ * @since 2.7.6
+ */
+public class StringToIntegerConverter implements StringConverter<Integer> {
+
+ @Override
+ public Integer convert(String source) {
+ return isNotEmpty(source) ? valueOf(source) : null;
+ }
+
+ @Override
+ public int getPriority() {
+ return NORMAL_PRIORITY;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToLongConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToLongConverter.java
new file mode 100644
index 0000000..f9ebdcd
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToLongConverter.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.common.convert;
+
+import static java.lang.Long.valueOf;
+import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
+
+/**
+ * The class to convert {@link String} to {@link Long}
+ *
+ * @since 2.7.6
+ */
+public class StringToLongConverter implements StringConverter<Long> {
+
+ @Override
+ public Long convert(String source) {
+ return isNotEmpty(source) ? valueOf(source) : null;
+ }
+
+
+ @Override
+ public int getPriority() {
+ return NORMAL_PRIORITY + 1;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToOptionalConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToOptionalConverter.java
new file mode 100644
index 0000000..a26fa04
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToOptionalConverter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.common.convert;
+
+import java.util.Optional;
+
+import static java.util.Optional.ofNullable;
+
+/**
+ * The class to convert {@link String} to {@link Optional}
+ *
+ * @since 2.7.6
+ */
+public class StringToOptionalConverter implements StringConverter<Optional> {
+
+ @Override
+ public Optional convert(String source) {
+ return ofNullable(source);
+ }
+
+
+ @Override
+ public int getPriority() {
+ return MIN_PRIORITY;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToShortConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToShortConverter.java
new file mode 100644
index 0000000..6427b17
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToShortConverter.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.common.convert;
+
+import static java.lang.Short.valueOf;
+import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
+
+/**
+ * The class to convert {@link String} to {@link Short}
+ *
+ * @since 2.7.6
+ */
+public class StringToShortConverter implements StringConverter<Short> {
+
+ @Override
+ public Short convert(String source) {
+ return isNotEmpty(source) ? valueOf(source) : null;
+ }
+
+
+ @Override
+ public int getPriority() {
+ return NORMAL_PRIORITY + 2;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToStringConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToStringConverter.java
new file mode 100644
index 0000000..d97d523
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/StringToStringConverter.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.common.convert;
+
+/**
+ * A class to covert {@link String} to {@link String} value, just no-op
+ *
+ * @since 2.7.6
+ */
+public class StringToStringConverter implements StringConverter<String> {
+
+ @Override
+ public String convert(String source) {
+ return source;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/MultiValueConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/MultiValueConverter.java
new file mode 100644
index 0000000..298b459
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/MultiValueConverter.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.common.convert.multiple;
+
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.common.lang.Prioritized;
+
+import java.util.Collection;
+
+import static org.apache.dubbo.common.utils.TypeUtils.findActualTypeArgument;
+
+/**
+ * An interface to convert the source-typed value to multiple value, e.g , Java array, {@link Collection} or
+ * sub-interfaces
+ *
+ * @param <S> The source type
+ * @since 2.7.6
+ */
+@SPI
+public interface MultiValueConverter<S> extends Prioritized {
+
+ /**
+ * Accept the source type and target type or not
+ *
+ * @param sourceType the source type
+ * @param multiValueType the multi-value type
+ * @return if accepted, return <code>true</code>, or <code>false</code>
+ */
+ boolean accept(Class<S> sourceType, Class<?> multiValueType);
+
+ /**
+ * Convert the source to be the multiple value
+ *
+ * @param source the source-typed value
+ * @param multiValueType the multi-value type
+ * @param elementType the element type
+ * @return
+ */
+ Object convert(S source, Class<?> multiValueType, Class<?> elementType);
+
+ /**
+ * Get the source type
+ *
+ * @return non-null
+ */
+ default Class<S> getSourceType() {
+ return findActualTypeArgument(getClass(), MultiValueConverter.class, 0);
+ }
+
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToArrayConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToArrayConverter.java
new file mode 100644
index 0000000..32490cd
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToArrayConverter.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.common.convert.multiple;
+
+import org.apache.dubbo.common.convert.Converter;
+
+import java.lang.reflect.Array;
+
+import static java.lang.reflect.Array.newInstance;
+
+/**
+ * The class to convert {@link String} to array-type object
+ *
+ * @since 2.7.6
+ */
+public class StringToArrayConverter implements StringToMultiValueConverter {
+
+ public boolean accept(Class<String> type, Class<?> multiValueType) {
+ if (multiValueType != null && multiValueType.isArray()) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Object convert(String[] segments, int size, Class<?> targetType, Class<?> elementType) {
+
+ Class<?> componentType = targetType.getComponentType();
+
+ Converter converter = Converter.getConverter(String.class, componentType);
+
+ Object array = newInstance(componentType, size);
+
+ for (int i = 0; i < size; i++) {
+ Array.set(array, i, converter.convert(segments[i]));
+ }
+
+ return array;
+ }
+
+
+ @Override
+ public int getPriority() {
+ return MIN_PRIORITY;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToBlockingDequeConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToBlockingDequeConverter.java
new file mode 100644
index 0000000..61e0a7d
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToBlockingDequeConverter.java
@@ -0,0 +1,33 @@
+/*
+ * 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.common.convert.multiple;
+
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.LinkedBlockingDeque;
+
+/**
+ * The class to convert {@link String} to {@link BlockingDeque}-based value
+ *
+ * @since 2.7.6
+ */
+public class StringToBlockingDequeConverter extends StringToIterableConverter<BlockingDeque> {
+
+ @Override
+ protected BlockingDeque createMultiValue(int size, Class<?> multiValueType) {
+ return new LinkedBlockingDeque(size);
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToBlockingQueueConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToBlockingQueueConverter.java
new file mode 100644
index 0000000..f099e84
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToBlockingQueueConverter.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.common.convert.multiple;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+
+/**
+ * The class to convert {@link String} to {@link BlockingDeque}-based value
+ *
+ * @since 2.7.6
+ */
+public class StringToBlockingQueueConverter extends StringToIterableConverter<BlockingQueue> {
+
+ @Override
+ protected BlockingQueue createMultiValue(int size, Class<?> multiValueType) {
+ return new ArrayBlockingQueue(size);
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToCollectionConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToCollectionConverter.java
new file mode 100644
index 0000000..478447e
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToCollectionConverter.java
@@ -0,0 +1,33 @@
+/*
+ * 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.common.convert.multiple;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * The class to convert {@link String} to {@link Collection}-based value
+ *
+ * @since 2.7.6
+ */
+public class StringToCollectionConverter extends StringToIterableConverter<Collection> {
+
+ @Override
+ protected Collection createMultiValue(int size, Class<?> multiValueType) {
+ return new ArrayList(size);
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToDequeConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToDequeConverter.java
new file mode 100644
index 0000000..19bdc33
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToDequeConverter.java
@@ -0,0 +1,33 @@
+/*
+ * 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.common.convert.multiple;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+/**
+ * The class to convert {@link String} to {@link Deque}-based value
+ *
+ * @since 2.7.6
+ */
+public class StringToDequeConverter extends StringToIterableConverter<Deque> {
+
+ @Override
+ protected Deque createMultiValue(int size, Class<?> multiValueType) {
+ return new ArrayDeque(size);
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToIterableConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToIterableConverter.java
new file mode 100644
index 0000000..f84b360
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToIterableConverter.java
@@ -0,0 +1,80 @@
+/*
+ * 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.common.convert.multiple;
+
+import org.apache.dubbo.common.convert.StringConverter;
+
+import java.util.Collection;
+import java.util.Optional;
+
+import static org.apache.dubbo.common.convert.Converter.getConverter;
+import static org.apache.dubbo.common.utils.ClassUtils.getAllInterfaces;
+import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom;
+import static org.apache.dubbo.common.utils.TypeUtils.findActualTypeArgument;
+
+/**
+ * The class to convert {@link String} to {@link Iterable}-based value
+ *
+ * @since 2.7.6
+ */
+public abstract class StringToIterableConverter<T extends Iterable> implements StringToMultiValueConverter {
+
+ public boolean accept(Class<String> type, Class<?> multiValueType) {
+ return isAssignableFrom(getSupportedType(), multiValueType);
+ }
+
+ @Override
+ public final Object convert(String[] segments, int size, Class<?> multiValueType, Class<?> elementType) {
+
+ Optional<StringConverter> stringConverter = getStringConverter(elementType);
+
+ return stringConverter.map(converter -> {
+
+ T convertedObject = createMultiValue(size, multiValueType);
+
+ if (convertedObject instanceof Collection) {
+ Collection collection = (Collection) convertedObject;
+ for (int i = 0; i < size; i++) {
+ String segment = segments[i];
+ Object element = converter.convert(segment);
+ collection.add(element);
+ }
+ return collection;
+ }
+
+ return convertedObject;
+ }).orElse(null);
+ }
+
+ protected abstract T createMultiValue(int size, Class<?> multiValueType);
+
+ protected Optional<StringConverter> getStringConverter(Class<?> elementType) {
+ StringConverter converter = (StringConverter) getConverter(String.class, elementType);
+ return Optional.ofNullable(converter);
+ }
+
+ protected final Class<T> getSupportedType() {
+ return findActualTypeArgument(getClass(), StringToIterableConverter.class, 0);
+ }
+
+ @Override
+ public final int getPriority() {
+ int level = getAllInterfaces(getSupportedType(), type ->
+ isAssignableFrom(Iterable.class, type)).size();
+ return MIN_PRIORITY - level;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToListConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToListConverter.java
new file mode 100644
index 0000000..111a3a7
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToListConverter.java
@@ -0,0 +1,33 @@
+/*
+ * 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.common.convert.multiple;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The class to convert {@link String} to {@link List}-based value
+ *
+ * @since 2.7.6
+ */
+public class StringToListConverter extends StringToIterableConverter<List> {
+
+ @Override
+ protected List createMultiValue(int size, Class<?> multiValueType) {
+ return new ArrayList(size);
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToMultiValueConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToMultiValueConverter.java
new file mode 100644
index 0000000..75fefe6
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToMultiValueConverter.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.common.convert.multiple;
+
+import org.apache.dubbo.common.utils.ArrayUtils;
+
+import static org.apache.dubbo.common.utils.StringUtils.isEmpty;
+import static org.apache.dubbo.common.utils.StringUtils.split;
+
+/**
+ * The class to convert {@link String} to multiple value object
+ *
+ * @see MultiValueConverter
+ * @since 2.7.6
+ */
+public interface StringToMultiValueConverter extends MultiValueConverter<String> {
+
+ @Override
+ default Object convert(String source, Class<?> multiValueType, Class<?> elementType) {
+
+ if (isEmpty(source)) {
+ return null;
+ }
+
+ // split by the comma
+ String[] segments = split(source, ',');
+
+ if (ArrayUtils.isEmpty(segments)) { // If empty array, create an array with only one element
+ segments = new String[]{source};
+ }
+
+ int size = segments.length;
+
+ return convert(segments, size, multiValueType, elementType);
+ }
+
+ /**
+ * Convert the segments to multiple value object
+ *
+ * @param segments the String array of content
+ * @param size the size of multiple value object
+ * @param targetType the target type
+ * @param elementType the element type
+ * @return multiple value object
+ */
+ Object convert(String[] segments, int size, Class<?> targetType, Class<?> elementType);
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToNavigableSetConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToNavigableSetConverter.java
new file mode 100644
index 0000000..ccd692e
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToNavigableSetConverter.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.common.convert.multiple;
+
+import java.util.NavigableSet;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * The class to convert {@link String} to {@link SortedSet}-based value
+ *
+ * @since 2.7.6
+ */
+public class StringToNavigableSetConverter extends StringToIterableConverter<NavigableSet> {
+
+ @Override
+ protected NavigableSet createMultiValue(int size, Class<?> multiValueType) {
+ return new TreeSet();
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToQueueConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToQueueConverter.java
new file mode 100644
index 0000000..3bd7eed
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToQueueConverter.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.common.convert.multiple;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Queue;
+
+/**
+ * The class to convert {@link String} to {@link Deque}-based value
+ *
+ * @since 2.7.6
+ */
+public class StringToQueueConverter extends StringToIterableConverter<Queue> {
+
+ @Override
+ protected Queue createMultiValue(int size, Class<?> multiValueType) {
+ return new ArrayDeque(size);
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToSetConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToSetConverter.java
new file mode 100644
index 0000000..dfffead
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToSetConverter.java
@@ -0,0 +1,33 @@
+/*
+ * 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.common.convert.multiple;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The class to convert {@link String} to {@link Set}-based value
+ *
+ * @since 2.7.6
+ */
+public class StringToSetConverter extends StringToIterableConverter<Set> {
+
+ @Override
+ protected Set createMultiValue(int size, Class<?> multiValueType) {
+ return new HashSet(size);
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToSortedSetConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToSortedSetConverter.java
new file mode 100644
index 0000000..12b48d6
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToSortedSetConverter.java
@@ -0,0 +1,33 @@
+/*
+ * 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.common.convert.multiple;
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * The class to convert {@link String} to {@link SortedSet}-based value
+ *
+ * @since 2.7.6
+ */
+public class StringToSortedSetConverter extends StringToIterableConverter<SortedSet> {
+
+ @Override
+ protected SortedSet createMultiValue(int size, Class<?> multiValueType) {
+ return new TreeSet();
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToTransferQueueConverter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToTransferQueueConverter.java
new file mode 100644
index 0000000..32ca09f
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/convert/multiple/StringToTransferQueueConverter.java
@@ -0,0 +1,33 @@
+/*
+ * 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.common.convert.multiple;
+
+import java.util.concurrent.LinkedTransferQueue;
+import java.util.concurrent.TransferQueue;
+
+/**
+ * The class to convert {@link String} to {@link TransferQueue}-based value
+ *
+ * @since 2.7.6
+ */
+public class StringToTransferQueueConverter extends StringToIterableConverter<TransferQueue> {
+
+ @Override
+ protected TransferQueue createMultiValue(int size, Class<?> multiValueType) {
+ return new LinkedTransferQueue();
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
index 595b9d0..842a43f 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
@@ -19,6 +19,7 @@ package org.apache.dubbo.common.extension;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.context.Lifecycle;
import org.apache.dubbo.common.extension.support.ActivateComparator;
+import org.apache.dubbo.common.lang.Prioritized;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.ArrayUtils;
@@ -40,7 +41,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -49,6 +51,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;
+import static java.util.Collections.sort;
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.REMOVE_VALUE_PREFIX;
@@ -442,14 +445,16 @@ public class ExtensionLoader<T> {
}
public Set<T> getSupportedExtensionInstances() {
- Set<T> instances = new HashSet<>();
+ List<T> instances = new LinkedList<>();
Set<String> supportedExtensions = getSupportedExtensions();
if (CollectionUtils.isNotEmpty(supportedExtensions)) {
for (String name : supportedExtensions) {
instances.add(getExtension(name));
}
}
- return instances;
+ // sort the Prioritized instances
+ sort(instances, Prioritized.COMPARATOR);
+ return new LinkedHashSet<>(instances);
}
/**
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java
new file mode 100644
index 0000000..600c8aa
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java
@@ -0,0 +1,452 @@
+/*
+ * 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.common.utils;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.lang.reflect.AnnotatedElement;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.unmodifiableList;
+import static org.apache.dubbo.common.function.Predicates.and;
+import static org.apache.dubbo.common.function.Streams.filterAll;
+import static org.apache.dubbo.common.function.Streams.filterFirst;
+import static org.apache.dubbo.common.utils.ClassUtils.getAllInheritedTypes;
+import static org.apache.dubbo.common.utils.ClassUtils.resolveClass;
+import static org.apache.dubbo.common.utils.CollectionUtils.first;
+import static org.apache.dubbo.common.utils.MethodUtils.invokeMethod;
+
+/**
+ * Commons Annotation Utilities class
+ *
+ * @since 2.7.6
+ */
+public interface AnnotationUtils {
+
+ /**
+ * Resolve the annotation type by the annotated element and resolved class name
+ *
+ * @param annotatedElement the annotated element
+ * @param annotationClassName the class name of annotation
+ * @param <A> the type of annotation
+ * @return If resolved, return the type of annotation, or <code>null</code>
+ */
+ static <A extends Annotation> Class<A> resolveAnnotationType(AnnotatedElement annotatedElement,
+ String annotationClassName) {
+ ClassLoader classLoader = annotatedElement.getClass().getClassLoader();
+ Class<?> annotationType = resolveClass(annotationClassName, classLoader);
+ if (annotationType == null || !Annotation.class.isAssignableFrom(annotationType)) {
+ return null;
+ }
+ return (Class<A>) annotationType;
+ }
+
+ /**
+ * Is the specified type a generic {@link Class type}
+ *
+ * @param annotatedElement the annotated element
+ * @return if <code>annotatedElement</code> is the {@link Class}, return <code>true</code>, or <code>false</code>
+ * @see ElementType#TYPE
+ */
+ static boolean isType(AnnotatedElement annotatedElement) {
+ return annotatedElement instanceof Class;
+ }
+
+ /**
+ * Is the type of specified annotation same to the expected type?
+ *
+ * @param annotation the specified {@link Annotation}
+ * @param annotationType the expected annotation type
+ * @return if same, return <code>true</code>, or <code>false</code>
+ */
+ static boolean isSameType(Annotation annotation, Class<? extends Annotation> annotationType) {
+ if (annotation == null || annotationType == null) {
+ return false;
+ }
+ return Objects.equals(annotation.annotationType(), annotationType);
+ }
+
+ /**
+ * Build an instance of {@link Predicate} to excluded annotation type
+ *
+ * @param excludedAnnotationType excluded annotation type
+ * @return non-null
+ */
+ static Predicate<Annotation> excludedType(Class<? extends Annotation> excludedAnnotationType) {
+ return annotation -> !isSameType(annotation, excludedAnnotationType);
+ }
+
+ /**
+ * Get the attribute from the specified {@link Annotation annotation}
+ *
+ * @param annotation the specified {@link Annotation annotation}
+ * @param attributeName the attribute name
+ * @param <T> the type of attribute
+ * @return the attribute value
+ * @throws IllegalArgumentException If the attribute name can't be found
+ */
+ static <T> T getAttribute(Annotation annotation, String attributeName) throws IllegalArgumentException {
+ return annotation == null ? null : invokeMethod(annotation, attributeName);
+ }
+
+ /**
+ * Get the "value" attribute from the specified {@link Annotation annotation}
+ *
+ * @param annotation the specified {@link Annotation annotation}
+ * @param <T> the type of attribute
+ * @return the value of "value" attribute
+ * @throws IllegalArgumentException If the attribute name can't be found
+ */
+ static <T> T getValue(Annotation annotation) throws IllegalArgumentException {
+ return getAttribute(annotation, "value");
+ }
+
+ /**
+ * Get the {@link Annotation} from the specified {@link AnnotatedElement the annotated element} and
+ * {@link Annotation annotation} class name
+ *
+ * @param annotatedElement {@link AnnotatedElement}
+ * @param annotationClassName the class name of annotation
+ * @param <A> The type of {@link Annotation}
+ * @return the {@link Annotation} if found
+ * @throws ClassCastException If the {@link Annotation annotation} type that client requires can't match actual type
+ */
+ static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, String annotationClassName)
+ throws ClassCastException {
+ Class<? extends Annotation> annotationType = resolveAnnotationType(annotatedElement, annotationClassName);
+ if (annotationType == null) {
+ return null;
+ }
+ return (A) annotatedElement.getAnnotation(annotationType);
+ }
+
+ /**
+ * Get annotations that are <em>directly present</em> on this element.
+ * This method ignores inherited annotations.
+ *
+ * @param annotatedElement the annotated element
+ * @param annotationsToFilter the annotations to filter
+ * @return non-null read-only {@link List}
+ */
+ static List<Annotation> getDeclaredAnnotations(AnnotatedElement annotatedElement,
+ Predicate<Annotation>... annotationsToFilter) {
+ if (annotatedElement == null) {
+ return emptyList();
+ }
+
+ return unmodifiableList(filterAll(asList(annotatedElement.getDeclaredAnnotations()), annotationsToFilter));
+ }
+
+ /**
+ * Get all directly declared annotations of the the annotated element, not including
+ * meta annotations.
+ *
+ * @param annotatedElement the annotated element
+ * @param annotationsToFilter the annotations to filter
+ * @return non-null read-only {@link List}
+ */
+ static List<Annotation> getAllDeclaredAnnotations(AnnotatedElement annotatedElement,
+ Predicate<Annotation>... annotationsToFilter) {
+ if (isType(annotatedElement)) {
+ return getAllDeclaredAnnotations((Class) annotatedElement, annotationsToFilter);
+ } else {
+ return getDeclaredAnnotations(annotatedElement, annotationsToFilter);
+ }
+ }
+
+ /**
+ * Get all directly declared annotations of the specified type and its' all hierarchical types, not including
+ * meta annotations.
+ *
+ * @param type the specified type
+ * @param annotationsToFilter the annotations to filter
+ * @return non-null read-only {@link List}
+ */
+ static List<Annotation> getAllDeclaredAnnotations(Class<?> type, Predicate<Annotation>... annotationsToFilter) {
+
+ if (type == null) {
+ return emptyList();
+ }
+
+ List<Annotation> allAnnotations = new LinkedList<>();
+
+ // All types
+ Set<Class<?>> allTypes = new LinkedHashSet<>();
+ // Add current type
+ allTypes.add(type);
+ // Add all inherited types
+ allTypes.addAll(getAllInheritedTypes(type, t -> !Object.class.equals(t)));
+
+ for (Class<?> t : allTypes) {
+ allAnnotations.addAll(getDeclaredAnnotations(t, annotationsToFilter));
+ }
+
+ return unmodifiableList(allAnnotations);
+ }
+
+
+ /**
+ * Get the meta-annotated {@link Annotation annotations} directly, excluding {@link Target}, {@link Retention}
+ * and {@link Documented}
+ *
+ * @param annotationType the {@link Annotation annotation} type
+ * @param metaAnnotationsToFilter the meta annotations to filter
+ * @return non-null read-only {@link List}
+ */
+ static List<Annotation> getMetaAnnotations(Class<? extends Annotation> annotationType,
+ Predicate<Annotation>... metaAnnotationsToFilter) {
+ return getDeclaredAnnotations(annotationType,
+ // Excludes the Java native annotation types or it causes the stack overflow, e.g,
+ // @Target annotates itself
+ excludedType(Target.class),
+ excludedType(Retention.class),
+ excludedType(Documented.class),
+ // Add other predicates
+ and(metaAnnotationsToFilter)
+ );
+ }
+
+ /**
+ * Get all meta annotations from the specified {@link Annotation annotation} type
+ *
+ * @param annotationType the {@link Annotation annotation} type
+ * @param annotationsToFilter the annotations to filter
+ * @return non-null read-only {@link List}
+ */
+ static List<Annotation> getAllMetaAnnotations(Class<? extends Annotation> annotationType,
+ Predicate<Annotation>... annotationsToFilter) {
+
+ List<Annotation> allMetaAnnotations = new LinkedList<>();
+
+ List<Annotation> metaAnnotations = getMetaAnnotations(annotationType);
+
+ allMetaAnnotations.addAll(metaAnnotations);
+
+ for (Annotation metaAnnotation : metaAnnotations) {
+ // Get the nested meta annotations recursively
+ allMetaAnnotations.addAll(getAllMetaAnnotations(metaAnnotation.annotationType()));
+ }
+
+ return unmodifiableList(filterAll(allMetaAnnotations, annotationsToFilter));
+ }
+
+ /**
+ * Find the annotation that is annotated on the specified element may be a meta-annotation
+ *
+ * @param annotatedElement the annotated element
+ * @param annotationClassName the class name of annotation
+ * @param <A> the required type of annotation
+ * @return If found, return first matched-type {@link Annotation annotation}, or <code>null</code>
+ */
+ static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, String annotationClassName) {
+ return findAnnotation(annotatedElement, resolveAnnotationType(annotatedElement, annotationClassName));
+ }
+
+ /**
+ * Find the annotation that is annotated on the specified element may be a meta-annotation
+ *
+ * @param annotatedElement the annotated element
+ * @param annotationType the type of annotation
+ * @param <A> the required type of annotation
+ * @return If found, return first matched-type {@link Annotation annotation}, or <code>null</code>
+ */
+ static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
+ return (A) filterFirst(getAllDeclaredAnnotations(annotatedElement), a -> isSameType(a, annotationType));
+ }
+
+ /**
+ * Find the meta annotations from the the {@link Annotation annotation} type by meta annotation type
+ *
+ * @param annotationType the {@link Annotation annotation} type
+ * @param metaAnnotationType the meta annotation type
+ * @param <A> the type of required annotation
+ * @return if found, return all matched results, or get an {@link Collections#emptyList() empty list}
+ */
+ static <A extends Annotation> List<A> findMetaAnnotations(Class<? extends Annotation> annotationType,
+ Class<A> metaAnnotationType) {
+ return (List<A>) getAllMetaAnnotations(annotationType, a -> isSameType(a, metaAnnotationType));
+ }
+
+ /**
+ * Find the meta annotations from the the the annotated element by meta annotation type
+ *
+ * @param annotatedElement the annotated element
+ * @param metaAnnotationType the meta annotation type
+ * @param <A> the type of required annotation
+ * @return if found, return all matched results, or get an {@link Collections#emptyList() empty list}
+ */
+ static <A extends Annotation> List<A> findMetaAnnotations(AnnotatedElement annotatedElement,
+ Class<A> metaAnnotationType) {
+ List<A> metaAnnotations = new LinkedList<>();
+
+ for (Annotation annotation : getAllDeclaredAnnotations(annotatedElement)) {
+ metaAnnotations.addAll(findMetaAnnotations(annotation.annotationType(), metaAnnotationType));
+ }
+
+ return unmodifiableList(metaAnnotations);
+ }
+
+ /**
+ * Find the meta annotation from the annotated element by meta annotation type
+ *
+ * @param annotatedElement the annotated element
+ * @param metaAnnotationClassName the class name of meta annotation
+ * @param <A> the type of required annotation
+ * @return {@link #findMetaAnnotation(Class, Class)}
+ */
+ static <A extends Annotation> A findMetaAnnotation(AnnotatedElement annotatedElement,
+ String metaAnnotationClassName) {
+ return findMetaAnnotation(annotatedElement, resolveAnnotationType(annotatedElement, metaAnnotationClassName));
+ }
+
+ /**
+ * Find the meta annotation from the annotation type by meta annotation type
+ *
+ * @param annotationType the {@link Annotation annotation} type
+ * @param metaAnnotationType the meta annotation type
+ * @param <A> the type of required annotation
+ * @return If found, return the {@link CollectionUtils#first(Collection)} matched result, return <code>null</code>.
+ * If it requires more result, please consider to use {@link #findMetaAnnotations(Class, Class)}
+ * @see #findMetaAnnotations(Class, Class)
+ */
+ static <A extends Annotation> A findMetaAnnotation(Class<? extends Annotation> annotationType,
+ Class<A> metaAnnotationType) {
+ return first(findMetaAnnotations(annotationType, metaAnnotationType));
+ }
+
+ /**
+ * Find the meta annotation from the annotated element by meta annotation type
+ *
+ * @param annotatedElement the annotated element
+ * @param metaAnnotationType the meta annotation type
+ * @param <A> the type of required annotation
+ * @return If found, return the {@link CollectionUtils#first(Collection)} matched result, return <code>null</code>.
+ * If it requires more result, please consider to use {@link #findMetaAnnotations(AnnotatedElement, Class)}
+ * @see #findMetaAnnotations(AnnotatedElement, Class)
+ */
+ static <A extends Annotation> A findMetaAnnotation(AnnotatedElement annotatedElement, Class<A> metaAnnotationType) {
+ return first(findMetaAnnotations(annotatedElement, metaAnnotationType));
+ }
+
+ /**
+ * Tests the annotated element is annotated the specified annotations or not
+ *
+ * @param type the annotated type
+ * @param matchAll If <code>true</code>, checking all annotation types are present or not, or match any
+ * @param annotationTypes the specified annotation types
+ * @return If the specified annotation types are present, return <code>true</code>, or <code>false</code>
+ */
+ static boolean isAnnotationPresent(Class<?> type,
+ boolean matchAll,
+ Class<? extends Annotation>... annotationTypes) {
+
+ int size = annotationTypes == null ? 0 : annotationTypes.length;
+
+ if (size < 1) {
+ return false;
+ }
+
+ int presentCount = 0;
+
+ for (int i = 0; i < size; i++) {
+ Class<? extends Annotation> annotationType = annotationTypes[i];
+ if (findAnnotation(type, annotationType) != null || findMetaAnnotation(type, annotationType) != null) {
+ presentCount++;
+ }
+ }
+
+ return matchAll ? presentCount == size : presentCount > 0;
+ }
+
+ /**
+ * Tests the annotated element is annotated the specified annotation or not
+ *
+ * @param type the annotated type
+ * @param annotationType the class of annotation
+ * @return If the specified annotation type is present, return <code>true</code>, or <code>false</code>
+ */
+ static boolean isAnnotationPresent(Class<?> type, Class<? extends Annotation> annotationType) {
+ return isAnnotationPresent(type, true, annotationType);
+ }
+
+ /**
+ * Tests the annotated element is present any specified annotation types
+ *
+ * @param annotatedElement the annotated element
+ * @param annotationClassName the class name of annotation
+ * @return If any specified annotation types are present, return <code>true</code>
+ */
+ static boolean isAnnotationPresent(AnnotatedElement annotatedElement, String annotationClassName) {
+ ClassLoader classLoader = annotatedElement.getClass().getClassLoader();
+ Class<?> resolvedType = resolveClass(annotationClassName, classLoader);
+ if (!Annotation.class.isAssignableFrom(resolvedType)) {
+ return false;
+ }
+ return isAnnotationPresent(annotatedElement, (Class<? extends Annotation>) resolvedType);
+ }
+
+ /**
+ * Tests the annotated element is present any specified annotation types
+ *
+ * @param annotatedElement the annotated element
+ * @param annotationType the class of annotation
+ * @return If any specified annotation types are present, return <code>true</code>
+ */
+ static boolean isAnnotationPresent(AnnotatedElement annotatedElement, Class<? extends Annotation> annotationType) {
+ if (isType(annotatedElement)) {
+ return isAnnotationPresent((Class) annotatedElement, annotationType);
+ } else {
+ return annotatedElement.isAnnotationPresent(annotationType) ||
+ findMetaAnnotation(annotatedElement, annotationType) != null; // to find meta-annotation
+ }
+ }
+
+ /**
+ * Tests the annotated element is annotated all specified annotations or not
+ *
+ * @param type the annotated type
+ * @param annotationTypes the specified annotation types
+ * @return If the specified annotation types are present, return <code>true</code>, or <code>false</code>
+ */
+ static boolean isAllAnnotationPresent(Class<?> type, Class<? extends Annotation>... annotationTypes) {
+ return isAnnotationPresent(type, true, annotationTypes);
+ }
+
+ /**
+ * Tests the annotated element is present any specified annotation types
+ *
+ * @param type the annotated type
+ * @param annotationTypes the specified annotation types
+ * @return If any specified annotation types are present, return <code>true</code>
+ */
+ static boolean isAnyAnnotationPresent(Class<?> type,
+ Class<? extends Annotation>... annotationTypes) {
+ return isAnnotationPresent(type, false, annotationTypes);
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CharSequenceComparator.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CharSequenceComparator.java
new file mode 100644
index 0000000..055ba48
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CharSequenceComparator.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.common.utils;
+
+import java.util.Comparator;
+
+/**
+ * The {@link Comparator} for {@link CharSequence}
+ *
+ * @since 2.7.6
+ */
+public class CharSequenceComparator implements Comparator<CharSequence> {
+
+ public final static CharSequenceComparator INSTANCE = new CharSequenceComparator();
+
+ private CharSequenceComparator() {
+ }
+
+ @Override
+ public int compare(CharSequence c1, CharSequence c2) {
+ return c1.toString().compareTo(c2.toString());
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassUtils.java
index ec5b766..a0b6dee 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassUtils.java
@@ -18,11 +18,26 @@ package org.apache.dubbo.common.utils;
import java.lang.reflect.Array;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
+import java.util.function.Predicate;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptySet;
+import static java.util.Collections.unmodifiableSet;
+import static java.util.stream.Collectors.toList;
+import static org.apache.dubbo.common.function.Streams.filterAll;
+import static org.apache.dubbo.common.utils.ArrayUtils.isNotEmpty;
+import static org.apache.dubbo.common.utils.CollectionUtils.ofSet;
public class ClassUtils {
/**
@@ -44,6 +59,43 @@ public class ClassUtils {
*/
private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_TYPE_MAP = new HashMap<Class<?>, Class<?>>(16);
+ /**
+ * Simple Types including:
+ * <ul>
+ * <li>{@link Void}</li>
+ * <li>{@link Boolean}</li>
+ * <li>{@link Character}</li>
+ * <li>{@link Byte}</li>
+ * <li>{@link Integer}</li>
+ * <li>{@link Float}</li>
+ * <li>{@link Double}</li>
+ * <li>{@link String}</li>
+ * <li>{@link BigDecimal}</li>
+ * <li>{@link BigInteger}</li>
+ * <li>{@link Date}</li>
+ * <li>{@link Object}</li>
+ * </ul>
+ *
+ * @see javax.management.openmbean.SimpleType
+ * @since 2.7.6
+ */
+ public static final Set<Class<?>> SIMPLE_TYPES = ofSet(
+ Void.class,
+ Boolean.class,
+ Character.class,
+ Byte.class,
+ Short.class,
+ Integer.class,
+ Long.class,
+ Float.class,
+ Double.class,
+ String.class,
+ BigDecimal.class,
+ BigInteger.class,
+ Date.class,
+ Object.class
+ );
+
private static final char PACKAGE_SEPARATOR_CHAR = '.';
static {
@@ -228,18 +280,27 @@ public class ClassUtils {
}
+ /**
+ * The specified type is primitive type or simple type
+ *
+ * @param type the type to test
+ * @return
+ * @deprecated as 2.7.6, use {@link Class#isPrimitive()} plus {@link #isSimpleType(Class)} instead
+ */
public static boolean isPrimitive(Class<?> type) {
- return type.isPrimitive()
- || type == String.class
- || type == Character.class
- || type == Boolean.class
- || type == Byte.class
- || type == Short.class
- || type == Integer.class
- || type == Long.class
- || type == Float.class
- || type == Double.class
- || type == Object.class;
+ return type != null && (type.isPrimitive() || isSimpleType(type));
+ }
+
+ /**
+ * The specified type is simple type or not
+ *
+ * @param type the type to test
+ * @return if <code>type</code> is one element of {@link #SIMPLE_TYPES}, return <code>true</code>, or <code>false</code>
+ * @see #SIMPLE_TYPES
+ * @since 2.7.6
+ */
+ public static boolean isSimpleType(Class<?> type) {
+ return SIMPLE_TYPES.contains(type);
}
public static Object convertPrimitive(Class<?> type, String value) {
@@ -285,4 +346,147 @@ public class ClassUtils {
}
return true;
}
+
+ /**
+ * Get all super classes from the specified type
+ *
+ * @param type the specified type
+ * @param classFilters the filters for classes
+ * @return non-null read-only {@link Set}
+ * @since 2.7.6
+ */
+ public static Set<Class<?>> getAllSuperClasses(Class<?> type, Predicate<Class<?>>... classFilters) {
+
+ Set<Class<?>> allSuperClasses = new LinkedHashSet<>();
+
+ Class<?> superClass = type.getSuperclass();
+
+ if (superClass != null) {
+ // add current super class
+ allSuperClasses.add(superClass);
+ // add ancestor classes
+ allSuperClasses.addAll(getAllSuperClasses(superClass));
+ }
+
+ return unmodifiableSet(filterAll(allSuperClasses, classFilters));
+ }
+
+ /**
+ * Get all interfaces from the specified type
+ *
+ * @param type the specified type
+ * @param interfaceFilters the filters for interfaces
+ * @return non-null read-only {@link Set}
+ * @since 2.7.6
+ */
+ public static Set<Class<?>> getAllInterfaces(Class<?> type, Predicate<Class<?>>... interfaceFilters) {
+
+ if (type == null || type.isPrimitive()) {
+ return emptySet();
+ }
+
+ Set<Class<?>> allInterfaces = new LinkedHashSet<>();
+
+ Class<?>[] interfaces = type.getInterfaces();
+
+ if (isNotEmpty(interfaces)) {
+ // add current interfaces
+ allInterfaces.addAll(asList(interfaces));
+ }
+
+ // add all super interfaces
+ getAllSuperClasses(type).forEach(superType -> allInterfaces.addAll(getAllInterfaces(superType)));
+
+ // add all super interfaces from all interfaces
+ allInterfaces.stream()
+ .map(ClassUtils::getAllInterfaces)
+ .flatMap(Collection::stream)
+ .collect(toList())
+ .forEach(allInterfaces::add);
+
+ return filterAll(allInterfaces, interfaceFilters);
+ }
+
+ /**
+ * Get all inherited types from the specified type
+ *
+ * @param type the specified type
+ * @param typeFilters the filters for types
+ * @return non-null read-only {@link Set}
+ * @since 2.7.6
+ */
+ public static Set<Class<?>> getAllInheritedTypes(Class<?> type, Predicate<Class<?>>... typeFilters) {
+ // Add all super classes
+ Set<Class<?>> types = new LinkedHashSet<>(getAllSuperClasses(type, typeFilters));
+ // Add all interface classes
+ types.addAll(getAllInterfaces(type, typeFilters));
+ return unmodifiableSet(types);
+ }
+
+
+ /**
+ * the semantics is same as {@link Class#isAssignableFrom(Class)}
+ *
+ * @param superType the super type
+ * @param targetType the target type
+ * @return see {@link Class#isAssignableFrom(Class)}
+ * @since 2.7.6
+ */
+ public static boolean isAssignableFrom(Class<?> superType, Class<?> targetType) {
+ // any argument is null
+ if (superType == null || targetType == null) {
+ return false;
+ }
+ // equals
+ if (Objects.equals(superType, targetType)) {
+ return true;
+ }
+ // isAssignableFrom
+ return superType.isAssignableFrom(targetType);
+ }
+
+ /**
+ * Test the specified class name is present in the {@link ClassLoader}
+ *
+ * @param className the name of {@link Class}
+ * @param classLoader {@link ClassLoader}
+ * @return If found, return <code>true</code>
+ * @since 2.7.6
+ */
+ public static boolean isPresent(String className, ClassLoader classLoader) {
+ try {
+ forName(className, classLoader);
+ } catch (Throwable ignored) { // Ignored
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Resolve the {@link Class} by the specified name and {@link ClassLoader}
+ *
+ * @param className the name of {@link Class}
+ * @param classLoader {@link ClassLoader}
+ * @return If can't be resolved , return <code>null</code>
+ * @since 2.7.6
+ */
+ public static Class<?> resolveClass(String className, ClassLoader classLoader) {
+ Class<?> targetClass = null;
+ try {
+ targetClass = forName(className, classLoader);
+ } catch (Throwable ignored) { // Ignored
+ }
+ return targetClass;
+ }
+
+ /**
+ * Is generic class or not?
+ *
+ * @param type the target type
+ * @return if the target type is not null or <code>void</code> or Void.class, return <code>true</code>, or false
+ * @since 2.7.6
+ */
+ public static boolean isGenericClass(Class<?> type) {
+ return type != null && !void.class.equals(type) && !Void.class.equals(type);
+ }
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CollectionUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CollectionUtils.java
index 9bb3c5e..1b73370 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CollectionUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CollectionUtils.java
@@ -16,6 +16,7 @@
*/
package org.apache.dubbo.common.utils;
+import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -26,7 +27,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
-import static java.util.Arrays.asList;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableSet;
@@ -270,9 +270,112 @@ public class CollectionUtils {
* @return read-only {@link Set}
*/
public static <T> Set<T> ofSet(T... values) {
- if (values == null || values.length < 1) {
+ int size = values == null ? 0 : values.length;
+ if (size < 1) {
return emptySet();
}
- return unmodifiableSet(new LinkedHashSet<>(asList(values)));
+
+ float loadFactor = 1f / ((size + 1) * 1.0f);
+
+ if (loadFactor > 0.75f) {
+ loadFactor = 0.75f;
+ }
+
+ Set<T> elements = new LinkedHashSet<>(size, loadFactor);
+ for (int i = 0; i < size; i++) {
+ elements.add(values[i]);
+ }
+ return unmodifiableSet(elements);
}
+
+ /**
+ * Get the size of the specified {@link Collection}
+ *
+ * @param collection the specified {@link Collection}
+ * @return must be positive number
+ * @since 2.7.6
+ */
+ public static int size(Collection<?> collection) {
+ return collection == null ? 0 : collection.size();
+ }
+
+ /**
+ * Compares the specified collection with another, the main implementation references
+ * {@link AbstractSet}
+ *
+ * @param one {@link Collection}
+ * @param another {@link Collection}
+ * @return if equals, return <code>true</code>, or <code>false</code>
+ * @since 2.7.6
+ */
+ public static boolean equals(Collection<?> one, Collection<?> another) {
+
+ if (one == another) {
+ return true;
+ }
+
+ if (isEmpty(one) && isEmpty(another)) {
+ return true;
+ }
+
+ if (size(one) != size(another)) {
+ return false;
+ }
+
+ try {
+ return one.containsAll(another);
+ } catch (ClassCastException unused) {
+ return false;
+ } catch (NullPointerException unused) {
+ return false;
+ }
+ }
+
+ /**
+ * Add the multiple values into {@link Collection the specified collection}
+ *
+ * @param collection {@link Collection the specified collection}
+ * @param values the multiple values
+ * @param <T> the type of values
+ * @return the effected count after added
+ * @since 2.7.6
+ */
+ public static <T> int addAll(Collection<T> collection, T... values) {
+
+ int size = values == null ? 0 : values.length;
+
+ if (collection == null || size < 1) {
+ return 0;
+ }
+
+ int effectedCount = 0;
+ for (int i = 0; i < size; i++) {
+ if (collection.add(values[i])) {
+ effectedCount++;
+ }
+ }
+
+ return effectedCount;
+ }
+
+ /**
+ * Take the first element from the specified collection
+ *
+ * @param values the collection object
+ * @param <T> the type of element of collection
+ * @return if found, return the first one, or <code>null</code>
+ * @since 2.7.6
+ */
+ public static <T> T first(Collection<T> values) {
+ if (isEmpty(values)) {
+ return null;
+ }
+ if (values instanceof List) {
+ List<T> list = (List<T>) values;
+ return list.get(0);
+ } else {
+ return values.iterator().next();
+ }
+ }
+
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/HttpUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/HttpUtils.java
new file mode 100644
index 0000000..b944d5a
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/HttpUtils.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2018 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.common.utils;
+
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.unmodifiableSet;
+import static org.apache.dubbo.common.utils.StringUtils.AND;
+import static org.apache.dubbo.common.utils.StringUtils.EQUAL;
+import static org.apache.dubbo.common.utils.StringUtils.QUESTION_MASK;
+import static org.apache.dubbo.common.utils.StringUtils.SLASH;
+import static org.apache.dubbo.common.utils.StringUtils.isEmpty;
+import static org.apache.dubbo.common.utils.StringUtils.replace;
+
+/**
+ * Http Utilities class
+ *
+ * @since 2.7.6
+ */
+public abstract class HttpUtils {
+
+ private static final String UTF_8 = "UTF-8";
+
+ /**
+ * HTTP GET method.
+ */
+ public static final String GET = "GET";
+ /**
+ * HTTP POST method.
+ */
+ public static final String POST = "POST";
+ /**
+ * HTTP PUT method.
+ */
+ public static final String PUT = "PUT";
+ /**
+ * HTTP DELETE method.
+ */
+ public static final String DELETE = "DELETE";
+ /**
+ * HTTP HEAD method.
+ */
+ public static final String HEAD = "HEAD";
+ /**
+ * HTTP OPTIONS method.
+ */
+ public static final String OPTIONS = "OPTIONS";
+
+ /**
+ * The HTTP methods to support
+ */
+ public static final Set<String> HTTP_METHODS = unmodifiableSet(new LinkedHashSet<>(asList(
+ GET, POST, POST, PUT, DELETE, HEAD, OPTIONS
+ )));
+
+
+ public static String buildPath(String rootPath, String... subPaths) {
+
+ Set<String> paths = new LinkedHashSet<>();
+ paths.add(rootPath);
+ paths.addAll(asList(subPaths));
+
+ return normalizePath(paths.stream()
+ .filter(StringUtils::isNotEmpty)
+ .collect(Collectors.joining(SLASH)));
+ }
+
+ /**
+ * Normalize path:
+ * <ol>
+ * <li>To remove query string if presents</li>
+ * <li>To remove duplicated slash("/") if exists</li>
+ * </ol>
+ *
+ * @param path path to be normalized
+ * @return a normalized path if required
+ */
+ public static String normalizePath(String path) {
+ if (isEmpty(path)) {
+ return SLASH;
+ }
+ String normalizedPath = path;
+ int index = normalizedPath.indexOf(QUESTION_MASK);
+ if (index > -1) {
+ normalizedPath = normalizedPath.substring(0, index);
+ }
+ return replace(normalizedPath, "//", "/");
+ }
+
+// /**
+// * Get Parameters from the specified query string.
+// * <p>
+// *
+// * @param queryString The query string
+// * @return The query parameters
+// */
+// public static MultivaluedMap<String, String> getParameters(String queryString) {
+// return getParameters(split(queryString, AND_CHAR));
+// }
+
+// /**
+// * Get Parameters from the specified pairs of name-value.
+// * <p>
+// *
+// * @param pairs The pairs of name-value
+// * @return The query parameters
+// */
+// public static MultivaluedMap<String, String> getParameters(Iterable<String> pairs) {
+// MultivaluedMap<String, String> parameters = new MultivaluedHashMap<>();
+// if (pairs != null) {
+// for (String pair : pairs) {
+// String[] nameAndValue = split(pair, EQUAL_CHAR);
+// String name = decode(nameAndValue[0]);
+// String value = nameAndValue.length < 2 ? null : nameAndValue[1];
+// value = decode(value);
+// addParam(parameters, name, value);
+// }
+// }
+// return parameters;
+// }
+
+// /**
+// * Get Parameters from the specified pairs of name-value.
+// * <p>
+// *
+// * @param pairs The pairs of name-value
+// * @return The query parameters
+// */
+// public static MultivaluedMap<String, String> getParameters(String... pairs) {
+// return getParameters(asList(pairs));
+// }
+
+ // /**
+ // * Parse a read-only {@link MultivaluedMap} of {@link HttpCookie} from {@link
+ // HttpHeaders}
+ // *
+ // * @param httpHeaders {@link HttpHeaders}
+ // * @return non-null, the key is a cookie name , the value is {@link HttpCookie}
+ // */
+ // public static MultivaluedMap<String, HttpCookie> parseCookies(HttpHeaders
+ // httpHeaders) {
+ //
+ // String cookie = httpHeaders.getFirst(COOKIE);
+ //
+ // String[] cookieNameAndValues = StringUtils.delimitedListToStringArray(cookie,
+ // SEMICOLON);
+ //
+ // MultivaluedMap<String, HttpCookie> cookies = new
+ // LinkedMultiValueMap<>(cookieNameAndValues.length);
+ //
+ // for (String cookeNameAndValue : cookieNameAndValues) {
+ // String[] nameAndValue =
+ // delimitedListToStringArray(trimWhitespace(cookeNameAndValue), EQUAL);
+ // String name = nameAndValue[0];
+ // String value = nameAndValue.length < 2 ? null : nameAndValue[1];
+ // HttpCookie httpCookie = new HttpCookie(name, value);
+ // cookies.add(name, httpCookie);
+ // }
+ //
+ // return cookies;
+ // }
+
+ /**
+ * To the name and value line sets
+ *
+ * @param nameAndValuesMap the map of name and values
+ * @return non-null
+ */
+ public static Set<String> toNameAndValuesSet(
+ Map<String, List<String>> nameAndValuesMap) {
+ Set<String> nameAndValues = new LinkedHashSet<>();
+ for (Map.Entry<String, List<String>> entry : nameAndValuesMap.entrySet()) {
+ String name = entry.getKey();
+ List<String> values = entry.getValue();
+ for (String value : values) {
+ String nameAndValue = name + EQUAL + value;
+ nameAndValues.add(nameAndValue);
+ }
+ }
+ return nameAndValues;
+ }
+
+ public static String[] toNameAndValues(Map<String, List<String>> nameAndValuesMap) {
+ return toNameAndValuesSet(nameAndValuesMap).toArray(new String[0]);
+ }
+
+ /**
+ * Generate a string of query string from the specified request parameters {@link Map}
+ *
+ * @param params the specified request parameters {@link Map}
+ * @return non-null
+ */
+ public static String toQueryString(Map<String, List<String>> params) {
+ StringBuilder builder = new StringBuilder();
+ for (String line : toNameAndValuesSet(params)) {
+ builder.append(line).append(AND);
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Decode value
+ *
+ * @param value the value requires to decode
+ * @return the decoded value
+ */
+ public static String decode(String value) {
+ if (value == null) {
+ return value;
+ }
+ String decodedValue = value;
+ try {
+ decodedValue = URLDecoder.decode(value, UTF_8);
+ } catch (UnsupportedEncodingException ex) {
+ }
+ return decodedValue;
+ }
+
+ /**
+ * encode value
+ *
+ * @param value the value requires to encode
+ * @return the encoded value
+ */
+ public static String encode(String value) {
+ String encodedValue = value;
+ try {
+ encodedValue = URLEncoder.encode(value, UTF_8);
+ } catch (UnsupportedEncodingException ex) {
+ }
+ return encodedValue;
+ }
+
+// private static void addParam(MultivaluedMap<String, String> paramsMap, String name,
+// String value) {
+// String paramValue = trim(value);
+// if (isEmpty(paramValue)) {
+// paramValue = EMPTY_VALUE;
+// }
+// paramsMap.add(trim(name), paramValue);
+// }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MemberUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MemberUtils.java
new file mode 100644
index 0000000..f7aa6ff
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MemberUtils.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.common.utils;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * Java Reflection {@link Member} Utilities class
+ *
+ * @since 2.7.6
+ */
+public interface MemberUtils {
+
+ /**
+ * check the specified {@link Member member} is static or not ?
+ *
+ * @param member {@link Member} instance, e.g, {@link Constructor}, {@link Method} or {@link Field}
+ * @return Iff <code>member</code> is static one, return <code>true</code>, or <code>false</code>
+ */
+ static boolean isStatic(Member member) {
+ return member != null && Modifier.isStatic(member.getModifiers());
+ }
+
+ /**
+ * check the specified {@link Member member} is private or not ?
+ *
+ * @param member {@link Member} instance, e.g, {@link Constructor}, {@link Method} or {@link Field}
+ * @return Iff <code>member</code> is private one, return <code>true</code>, or <code>false</code>
+ */
+ static boolean isPrivate(Member member) {
+ return member != null && Modifier.isPrivate(member.getModifiers());
+ }
+
+ /**
+ * check the specified {@link Member member} is public or not ?
+ *
+ * @param member {@link Member} instance, e.g, {@link Constructor}, {@link Method} or {@link Field}
+ * @return Iff <code>member</code> is public one, return <code>true</code>, or <code>false</code>
+ */
+ static boolean isPublic(Member member) {
+ return member != null && Modifier.isPublic(member.getModifiers());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodComparator.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodComparator.java
new file mode 100644
index 0000000..8270c1d
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodComparator.java
@@ -0,0 +1,71 @@
+/*
+ * 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.common.utils;
+
+import java.lang.reflect.Method;
+import java.util.Comparator;
+
+/**
+ * The Comparator class for {@link Method}, the comparison rule :
+ * <ol>
+ * <li>Comparing to two {@link Method#getName() method names} {@link String#compareTo(String) lexicographically}.
+ * If equals, go to step 2</li>
+ * <li>Comparing to the count of two method parameters. If equals, go to step 3</li>
+ * <li>Comparing to the type names of methods parameter {@link String#compareTo(String) lexicographically}</li>
+ * </ol>
+ *
+ * @since 2.7.6
+ */
+public class MethodComparator implements Comparator<Method> {
+
+ public final static MethodComparator INSTANCE = new MethodComparator();
+
+ private MethodComparator() {
+ }
+
+ @Override
+ public int compare(Method m1, Method m2) {
+
+ if (m1.equals(m2)) {
+ return 0;
+ }
+
+ // Step 1
+ String n1 = m1.getName();
+ String n2 = m2.getName();
+ int value = n1.compareTo(n2);
+
+ if (value == 0) { // Step 2
+
+ Class[] types1 = m1.getParameterTypes();
+ Class[] types2 = m2.getParameterTypes();
+
+ value = types1.length - types2.length;
+
+ if (value == 0) { // Step 3
+ for (int i = 0; i < types1.length; i++) {
+ value = types1[i].getName().compareTo(types2[i].getName());
+ if (value != 0) {
+ break;
+ }
+ }
+ }
+ }
+
+ return Integer.compare(value, 0);
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java
index 26b05ef..27262b4 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java
@@ -16,17 +16,32 @@
*/
package org.apache.dubbo.common.utils;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.unmodifiableList;
+import static org.apache.dubbo.common.function.Streams.filterAll;
+import static org.apache.dubbo.common.utils.ClassUtils.getAllInheritedTypes;
+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.ReflectUtils.EMPTY_CLASS_ARRAY;
+import static org.apache.dubbo.common.utils.ReflectUtils.resolveTypes;
/**
* Miscellaneous method utility methods.
* Mainly for internal use within the framework.
*
- * @author LiZhenNet
* @since 2.7.2
*/
-public class MethodUtils {
+public interface MethodUtils {
/**
* Return {@code true} if the provided method is a set method.
@@ -101,4 +116,277 @@ public class MethodUtils {
public static boolean isDeprecated(Method method) {
return method.getAnnotation(Deprecated.class) != null;
}
+
+
+
+ /**
+ * Create an instance of {@link Predicate} for {@link Method} to exclude the specified declared class
+ *
+ * @param declaredClass the declared class to exclude
+ * @return non-null
+ * @since 2.7.6
+ */
+ static Predicate<Method> excludedDeclaredClass(Class<?> declaredClass) {
+ return method -> !Objects.equals(declaredClass, method.getDeclaringClass());
+ }
+
+ /**
+ * Get all {@link Method methods} of the declared class
+ *
+ * @param declaringClass the declared class
+ * @param includeInheritedTypes include the inherited types, e,g. super classes or interfaces
+ * @param publicOnly only public method
+ * @param methodsToFilter (optional) the methods to be filtered
+ * @return non-null read-only {@link List}
+ * @since 2.7.6
+ */
+ static List<Method> getMethods(Class<?> declaringClass, boolean includeInheritedTypes, boolean publicOnly,
+ Predicate<Method>... methodsToFilter) {
+
+ if (declaringClass == null || declaringClass.isPrimitive()) {
+ return emptyList();
+ }
+
+ // All declared classes
+ List<Class<?>> declaredClasses = new LinkedList<>();
+ // Add the top declaring class
+ declaredClasses.add(declaringClass);
+ // If the super classes are resolved, all them into declaredClasses
+ if (includeInheritedTypes) {
+ declaredClasses.addAll(getAllInheritedTypes(declaringClass));
+ }
+
+ // All methods
+ List<Method> allMethods = new LinkedList<>();
+
+ for (Class<?> classToSearch : declaredClasses) {
+ Method[] methods = publicOnly ? classToSearch.getMethods() : classToSearch.getDeclaredMethods();
+ // Add the declared methods or public methods
+ for (Method method : methods) {
+ allMethods.add(method);
+ }
+ }
+
+ return unmodifiableList(filterAll(allMethods, methodsToFilter));
+ }
+
+ /**
+ * Get all declared {@link Method methods} of the declared class, excluding the inherited methods
+ *
+ * @param declaringClass the declared class
+ * @param methodsToFilter (optional) the methods to be filtered
+ * @return non-null read-only {@link List}
+ * @see #getMethods(Class, boolean, boolean, Predicate[])
+ * @since 2.7.6
+ */
+ static List<Method> getDeclaredMethods(Class<?> declaringClass, Predicate<Method>... methodsToFilter) {
+ return getMethods(declaringClass, false, false, methodsToFilter);
+ }
+
+ /**
+ * Get all public {@link Method methods} of the declared class, including the inherited methods.
+ *
+ * @param declaringClass the declared class
+ * @param methodsToFilter (optional) the methods to be filtered
+ * @return non-null read-only {@link List}
+ * @see #getMethods(Class, boolean, boolean, Predicate[])
+ * @since 2.7.6
+ */
+ static List<Method> getMethods(Class<?> declaringClass, Predicate<Method>... methodsToFilter) {
+ return getMethods(declaringClass, false, true, methodsToFilter);
+ }
+
+ /**
+ * Get all declared {@link Method methods} of the declared class, including the inherited methods.
+ *
+ * @param declaringClass the declared class
+ * @param methodsToFilter (optional) the methods to be filtered
+ * @return non-null read-only {@link List}
+ * @see #getMethods(Class, boolean, boolean, Predicate[])
+ * @since 2.7.6
+ */
+ static List<Method> getAllDeclaredMethods(Class<?> declaringClass, Predicate<Method>... methodsToFilter) {
+ return getMethods(declaringClass, true, false, methodsToFilter);
+ }
+
+ /**
+ * Get all public {@link Method methods} of the declared class, including the inherited methods.
+ *
+ * @param declaringClass the declared class
+ * @param methodsToFilter (optional) the methods to be filtered
+ * @return non-null read-only {@link List}
+ * @see #getMethods(Class, boolean, boolean, Predicate[])
+ * @since 2.7.6
+ */
+ static List<Method> getAllMethods(Class<?> declaringClass, Predicate<Method>... methodsToFilter) {
+ return getMethods(declaringClass, true, true, methodsToFilter);
+ }
+
+// static List<Method> getOverriderMethods(Class<?> implementationClass, Class<?>... superTypes) {
+
+//
+
+// }
+
+ /**
+ * Find the {@link Method} by the the specified type and method name without the parameter types
+ *
+ * @param type the target type
+ * @param methodName the specified method name
+ * @return if not found, return <code>null</code>
+ * @since 2.7.6
+ */
+ static Method findMethod(Class type, String methodName) {
+ return findMethod(type, methodName, EMPTY_CLASS_ARRAY);
+ }
+
+ /**
+ * Find the {@link Method} by the the specified type, method name and parameter types
+ *
+ * @param type the target type
+ * @param methodName the method name
+ * @param parameterTypes the parameter types
+ * @return if not found, return <code>null</code>
+ * @since 2.7.6
+ */
+ static Method findMethod(Class type, String methodName, Class<?>... parameterTypes) {
+ Method method = null;
+ try {
+ method = type.getDeclaredMethod(methodName, parameterTypes);
+ } catch (NoSuchMethodException e) {
+ }
+ return method;
+ }
+
+ /**
+ * Invoke the target object and method
+ *
+ * @param object the target object
+ * @param methodName the method name
+ * @param methodParameters the method parameters
+ * @param <T> the return type
+ * @return the target method's execution result
+ * @since 2.7.6
+ */
+ static <T> T invokeMethod(Object object, String methodName, Object... methodParameters) {
+ Class type = object.getClass();
+ Class[] parameterTypes = resolveTypes(methodParameters);
+ Method method = findMethod(type, methodName, parameterTypes);
+ T value = null;
+
+ try {
+ final boolean isAccessible = method.isAccessible();
+
+ if (!isAccessible) {
+ method.setAccessible(true);
+ }
+ value = (T) method.invoke(object, methodParameters);
+ method.setAccessible(isAccessible);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+
+ return value;
+ }
+
+
+ /**
+ * Tests whether one method, as a member of a given type,
+ * overrides another method.
+ *
+ * @param overrider the first method, possible overrider
+ * @param overridden the second method, possibly being overridden
+ * @return {@code true} if and only if the first method overrides
+ * the second
+ * @jls 8.4.8 Inheritance, Overriding, and Hiding
+ * @jls 9.4.1 Inheritance and Overriding
+ * @see Elements#overrides(ExecutableElement, ExecutableElement, TypeElement)
+ */
+ static boolean overrides(Method overrider, Method overridden) {
+
+ if (overrider == null || overridden == null) {
+ return false;
+ }
+
+ // equality comparison: If two methods are same
+ if (Objects.equals(overrider, overridden)) {
+ return false;
+ }
+
+ // Modifiers comparison: Any method must be non-static method
+ if (isStatic(overrider) || isStatic(overridden)) { //
+ return false;
+ }
+
+ // Modifiers comparison: the accessibility of any method must not be private
+ if (isPrivate(overrider) || isPrivate(overridden)) {
+ return false;
+ }
+
+ // Inheritance comparison: The declaring class of overrider must be inherit from the overridden's
+ if (!overridden.getDeclaringClass().isAssignableFrom(overrider.getDeclaringClass())) {
+ return false;
+ }
+
+ // Method comparison: must not be "default" method
+ if (overrider.isDefault()) {
+ return false;
+ }
+
+ // Method comparison: The method name must be equal
+ if (!Objects.equals(overrider.getName(), overridden.getName())) {
+ return false;
+ }
+
+ // Method comparison: The count of method parameters must be equal
+ if (!Objects.equals(overrider.getParameterCount(), overridden.getParameterCount())) {
+ return false;
+ }
+
+ // Method comparison: Any parameter type of overrider must equal the overridden's
+ for (int i = 0; i < overrider.getParameterCount(); i++) {
+ if (!Objects.equals(overridden.getParameterTypes()[i], overrider.getParameterTypes()[i])) {
+ return false;
+ }
+ }
+
+ // Method comparison: The return type of overrider must be inherit from the overridden's
+ if (!overridden.getReturnType().isAssignableFrom(overrider.getReturnType())) {
+ return false;
+ }
+
+ // Throwable comparison: "throws" Throwable list will be ignored, trust the compiler verify
+
+ return true;
+ }
+
+ /**
+ * Find the nearest overridden {@link Method method} from the inherited class
+ *
+ * @param overrider the overrider {@link Method method}
+ * @return if found, the overrider <code>method</code>, or <code>null</code>
+ */
+ static Method findNearestOverriddenMethod(Method overrider) {
+ Class<?> declaringClass = overrider.getDeclaringClass();
+ Method overriddenMethod = null;
+ for (Class<?> inheritedType : getAllInheritedTypes(declaringClass)) {
+ overriddenMethod = findOverriddenMethod(overrider, inheritedType);
+ if (overriddenMethod != null) {
+ break;
+ }
+ }
+ return overriddenMethod;
+ }
+
+ /**
+ * Find the overridden {@link Method method} from the declaring class
+ *
+ * @param overrider the overrider {@link Method method}
+ * @param declaringClass the class that is declaring the overridden {@link Method method}
+ * @return if found, the overrider <code>method</code>, or <code>null</code>
+ */
+ static Method findOverriddenMethod(Method overrider, Class<?> declaringClass) {
+ List<Method> matchedMethods = getAllMethods(declaringClass, method -> overrides(overrider, method));
+ return matchedMethods.isEmpty() ? null : matchedMethods.get(0);
+ }
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java
index df5764a..866d4b6 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java
@@ -55,6 +55,7 @@ import java.util.stream.Stream;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableSet;
+import static org.apache.dubbo.common.utils.ArrayUtils.isEmpty;
/**
* ReflectUtils
@@ -1259,6 +1260,15 @@ public final class ReflectUtils {
return unmodifiableSet(hierarchicalTypes);
}
+ /**
+ * Get the value from the specified bean and its property.
+ *
+ * @param bean the bean instance
+ * @param propertyName the name of property
+ * @param <T> the type of property value
+ * @return
+ * @since 2.7.5
+ */
public static <T> T getProperty(Object bean, String propertyName) {
Class<?> beanClass = bean.getClass();
BeanInfo beanInfo = null;
@@ -1282,4 +1292,28 @@ public final class ReflectUtils {
return propertyValue;
}
+ /**
+ * Resolve the types of the specified values
+ *
+ * @param values the values
+ * @return If can't be resolved, return {@link ReflectUtils#EMPTY_CLASS_ARRAY empty class array}
+ * @since 2.7.6
+ */
+ public static Class[] resolveTypes(Object... values) {
+
+ if (isEmpty(values)) {
+ return EMPTY_CLASS_ARRAY;
+ }
+
+ int size = values.length;
+
+ Class[] types = new Class[size];
+
+ for (int i = 0; i < size; i++) {
+ Object value = values[i];
+ types[i] = value == null ? null : value.getClass();
+ }
+
+ return types;
+ }
}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ServiceAnnotationResolver.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ServiceAnnotationResolver.java
new file mode 100644
index 0000000..58e5e7a
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ServiceAnnotationResolver.java
@@ -0,0 +1,119 @@
+/*
+ * 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.common.utils;
+
+import org.apache.dubbo.config.annotation.Service;
+
+import java.lang.annotation.Annotation;
+
+import static java.lang.String.format;
+import static org.apache.dubbo.common.utils.AnnotationUtils.getAttribute;
+import static org.apache.dubbo.common.utils.ArrayUtils.isNotEmpty;
+import static org.apache.dubbo.common.utils.ClassUtils.isGenericClass;
+import static org.apache.dubbo.common.utils.ClassUtils.resolveClass;
+import static org.apache.dubbo.common.utils.StringUtils.isEmpty;
+
+/**
+ * The resolver class for {@link Service @Service}
+ *
+ * @see Service
+ * @see com.alibaba.dubbo.config.annotation.Service
+ * @since 2.7.6
+ */
+public class ServiceAnnotationResolver {
+
+ private final Annotation serviceAnnotation;
+
+ private final Class<?> serviceType;
+
+ public ServiceAnnotationResolver(Class<?> serviceType) throws IllegalArgumentException {
+ this.serviceType = serviceType;
+ this.serviceAnnotation = getServiceAnnotation(serviceType);
+ }
+
+ private Annotation getServiceAnnotation(Class<?> serviceType) {
+
+ Annotation serviceAnnotation = serviceType.getAnnotation(Service.class);
+
+ if (serviceAnnotation == null) {
+ serviceAnnotation = serviceType.getAnnotation(com.alibaba.dubbo.config.annotation.Service.class);
+ }
+
+ if (serviceAnnotation == null) {
+ throw new IllegalArgumentException(format("@%s or @%s can't be found in the service type[%s].",
+ Service.class.getName(),
+ com.alibaba.dubbo.config.annotation.Service.class.getName(),
+ serviceType.getName()
+ ));
+ }
+
+ return serviceAnnotation;
+ }
+
+ /**
+ * Resolve the class name of interface
+ *
+ * @return if not found, return <code>null</code>
+ */
+ public String resolveInterfaceClassName() {
+
+ Class interfaceClass = null;
+ // first, try to get the value from "interfaceName" attribute
+ String interfaceName = resolveAttribute("interfaceName");
+
+ if (isEmpty(interfaceName)) { // If not found, try "interfaceClass"
+ interfaceClass = resolveAttribute("interfaceClass");
+ } else {
+ interfaceClass = resolveClass(interfaceName, getClass().getClassLoader());
+ }
+
+ if (isGenericClass(interfaceClass)) {
+ interfaceName = interfaceClass.getName();
+ } else {
+ interfaceName = null;
+ }
+
+ if (isEmpty(interfaceName)) { // If not fund, try to get the first interface from the service type
+ Class[] interfaces = serviceType.getInterfaces();
+ if (isNotEmpty(interfaces)) {
+ interfaceName = interfaces[0].getName();
+ }
+ }
+
+ return interfaceName;
+ }
+
+ public String resolveVersion() {
+ return resolveAttribute("version");
+ }
+
+ public String resolveGroup() {
+ return resolveAttribute("group");
+ }
+
+ private <T> T resolveAttribute(String attributeName) {
+ return getAttribute(serviceAnnotation, attributeName);
+ }
+
+ public Annotation getServiceAnnotation() {
+ return serviceAnnotation;
+ }
+
+ public Class<?> getServiceType() {
+ return serviceType;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/TypeUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/TypeUtils.java
new file mode 100644
index 0000000..2e53296
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/TypeUtils.java
@@ -0,0 +1,224 @@
+/*
+ * 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.common.utils;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.unmodifiableList;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static java.util.stream.StreamSupport.stream;
+import static org.apache.dubbo.common.function.Predicates.and;
+import static org.apache.dubbo.common.function.Streams.filterAll;
+import static org.apache.dubbo.common.function.Streams.filterList;
+import static org.apache.dubbo.common.utils.ClassUtils.getAllInterfaces;
+import static org.apache.dubbo.common.utils.ClassUtils.getAllSuperClasses;
+import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom;
+
+/**
+ * The utilities class for {@link Type}
+ *
+ * @since 2.7.6
+ */
+public interface TypeUtils {
+
+ Predicate<Class<?>> NON_OBJECT_TYPE_FILTER = t -> !Objects.equals(Object.class, t);
+
+ static boolean isParameterizedType(Type type) {
+ return type instanceof ParameterizedType;
+ }
+
+ static Type getRawType(Type type) {
+ if (isParameterizedType(type)) {
+ return ((ParameterizedType) type).getRawType();
+ } else {
+ return type;
+ }
+ }
+
+ static Class<?> getRawClass(Type type) {
+ Type rawType = getRawType(type);
+ if (isClass(rawType)) {
+ return (Class) rawType;
+ }
+ return null;
+ }
+
+ static boolean isClass(Type type) {
+ return type instanceof Class;
+ }
+
+ static <T> Class<T> findActualTypeArgument(Type type, Class<?> interfaceClass, int index) {
+ return (Class<T>) findActualTypeArguments(type, interfaceClass).get(index);
+ }
+
+ static List<Class<?>> findActualTypeArguments(Type type, Class<?> interfaceClass) {
+
+ List<Class<?>> actualTypeArguments = new LinkedList<>();
+
+ getAllGenericTypes(type, t -> isAssignableFrom(interfaceClass, getRawClass(t)))
+ .forEach(parameterizedType -> {
+ Class<?> rawClass = getRawClass(parameterizedType);
+ Type[] typeArguments = parameterizedType.getActualTypeArguments();
+ for (int i = 0; i < typeArguments.length; i++) {
+ Type typeArgument = typeArguments[i];
+ if (typeArgument instanceof Class) {
+ actualTypeArguments.add(i, (Class) typeArgument);
+ }
+ }
+ Class<?> superClass = rawClass.getSuperclass();
+ if (superClass != null) {
+ actualTypeArguments.addAll(findActualTypeArguments(superClass, interfaceClass));
+ }
+ });
+
+ return unmodifiableList(actualTypeArguments);
+ }
+
+ /**
+ * Get the specified types' generic types(including super classes and interfaces) that are assignable from {@link ParameterizedType} interface
+ *
+ * @param type the specified type
+ * @param typeFilters one or more {@link Predicate}s to filter the {@link ParameterizedType} instance
+ * @return non-null read-only {@link List}
+ */
+ static List<ParameterizedType> getGenericTypes(Type type, Predicate<ParameterizedType>... typeFilters) {
+
+ Class<?> rawClass = getRawClass(type);
+
+ if (rawClass == null) {
+ return emptyList();
+ }
+
+ List<Type> genericTypes = new LinkedList<>();
+
+ genericTypes.add(rawClass.getGenericSuperclass());
+ genericTypes.addAll(asList(rawClass.getGenericInterfaces()));
+
+ return unmodifiableList(
+ filterList(genericTypes, TypeUtils::isParameterizedType)
+ .stream()
+ .map(ParameterizedType.class::cast)
+ .filter(and(typeFilters))
+ .collect(toList())
+ );
+ }
+
+ /**
+ * Get all generic types(including super classes and interfaces) that are assignable from {@link ParameterizedType} interface
+ *
+ * @param type the specified type
+ * @param typeFilters one or more {@link Predicate}s to filter the {@link ParameterizedType} instance
+ * @return non-null read-only {@link List}
+ */
+ static List<ParameterizedType> getAllGenericTypes(Type type, Predicate<ParameterizedType>... typeFilters) {
+ List<ParameterizedType> allGenericTypes = new LinkedList<>();
+ // Add generic super classes
+ allGenericTypes.addAll(getAllGenericSuperClasses(type, typeFilters));
+ // Add generic super interfaces
+ allGenericTypes.addAll(getAllGenericInterfaces(type, typeFilters));
+ // wrap unmodifiable object
+ return unmodifiableList(allGenericTypes);
+ }
+
+ /**
+ * Get all generic super classes that are assignable from {@link ParameterizedType} interface
+ *
+ * @param type the specified type
+ * @param typeFilters one or more {@link Predicate}s to filter the {@link ParameterizedType} instance
+ * @return non-null read-only {@link List}
+ */
+ static List<ParameterizedType> getAllGenericSuperClasses(Type type, Predicate<ParameterizedType>... typeFilters) {
+
+ Class<?> rawClass = getRawClass(type);
+
+ if (rawClass == null || rawClass.isInterface()) {
+ return emptyList();
+ }
+
+ List<Class<?>> allTypes = new LinkedList<>();
+ // Add current class
+ allTypes.add(rawClass);
+ // Add all super classes
+ allTypes.addAll(getAllSuperClasses(rawClass, NON_OBJECT_TYPE_FILTER));
+
+ List<ParameterizedType> allGenericSuperClasses = allTypes
+ .stream()
+ .map(Class::getGenericSuperclass)
+ .filter(TypeUtils::isParameterizedType)
+ .map(ParameterizedType.class::cast)
+ .collect(Collectors.toList());
+
+ return unmodifiableList(filterAll(allGenericSuperClasses, typeFilters));
+ }
+
+ /**
+ * Get all generic interfaces that are assignable from {@link ParameterizedType} interface
+ *
+ * @param type the specified type
+ * @param typeFilters one or more {@link Predicate}s to filter the {@link ParameterizedType} instance
+ * @return non-null read-only {@link List}
+ */
+ static List<ParameterizedType> getAllGenericInterfaces(Type type, Predicate<ParameterizedType>... typeFilters) {
+
+ Class<?> rawClass = getRawClass(type);
+
+ if (rawClass == null) {
+ return emptyList();
+ }
+
+ List<Class<?>> allTypes = new LinkedList<>();
+ // Add current class
+ allTypes.add(rawClass);
+ // Add all super classes
+ allTypes.addAll(getAllSuperClasses(rawClass, NON_OBJECT_TYPE_FILTER));
+ // Add all super interfaces
+ allTypes.addAll(getAllInterfaces(rawClass));
+
+ List<ParameterizedType> allGenericInterfaces = allTypes
+ .stream()
+ .map(Class::getGenericInterfaces)
+ .map(Arrays::asList)
+ .flatMap(Collection::stream)
+ .filter(TypeUtils::isParameterizedType)
+ .map(ParameterizedType.class::cast)
+ .collect(toList());
+
+ return unmodifiableList(filterAll(allGenericInterfaces, typeFilters));
+ }
+
+ static String getClassName(Type type) {
+ return getRawType(type).getTypeName();
+ }
+
+ static Set<String> getClassNames(Iterable<? extends Type> types) {
+ return stream(types.spliterator(), false)
+ .map(TypeUtils::getClassName)
+ .collect(toSet());
+ }
+}
diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.convert.Converter b/dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.convert.Converter
new file mode 100644
index 0000000..f1ad0cc
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.convert.Converter
@@ -0,0 +1,11 @@
+# org.apache.dubbo.common.convert.Converter
+string-to-boolean=org.apache.dubbo.common.convert.StringToBooleanConverter
+string-to-character=org.apache.dubbo.common.convert.StringToCharacterConverter
+string-to-char-array=org.apache.dubbo.common.convert.StringToCharArrayConverter
+string-to-double=org.apache.dubbo.common.convert.StringToDoubleConverter
+string-to-float=org.apache.dubbo.common.convert.StringToFloatConverter
+string-to-integer=org.apache.dubbo.common.convert.StringToIntegerConverter
+string-to-long=org.apache.dubbo.common.convert.StringToLongConverter
+string-to-optional=org.apache.dubbo.common.convert.StringToOptionalConverter
+string-to-short=org.apache.dubbo.common.convert.StringToShortConverter
+string-to-string=org.apache.dubbo.common.convert.StringToStringConverter
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.convert.multiple.MultiValueConverter b/dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.convert.multiple.MultiValueConverter
new file mode 100644
index 0000000..d7f7692
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.convert.multiple.MultiValueConverter
@@ -0,0 +1,12 @@
+# org.apache.dubbo.common.convert.multiple.MultiValueConverter
+string-to-array=org.apache.dubbo.common.convert.multiple.StringToArrayConverter
+string-to-blocking-deque=org.apache.dubbo.common.convert.multiple.StringToBlockingDequeConverter
+string-to-blocking-queue=org.apache.dubbo.common.convert.multiple.StringToBlockingQueueConverter
+string-to-collection=org.apache.dubbo.common.convert.multiple.StringToCollectionConverter
+string-to-deque=org.apache.dubbo.common.convert.multiple.StringToDequeConverter
+string-to-list=org.apache.dubbo.common.convert.multiple.StringToListConverter
+string-to-navigable-set=org.apache.dubbo.common.convert.multiple.StringToNavigableSetConverter
+string-to-queue=org.apache.dubbo.common.convert.multiple.StringToQueueConverter
+string-to-set=org.apache.dubbo.common.convert.multiple.StringToSetConverter
+string-to-sorted-set=org.apache.dubbo.common.convert.multiple.StringToSortedSetConverter
+string-to-transfer-queue=org.apache.dubbo.common.convert.multiple.StringToTransferQueueConverter
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/AnnotationUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/AnnotationUtilsTest.java
new file mode 100644
index 0000000..0401763
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/AnnotationUtilsTest.java
@@ -0,0 +1,343 @@
+/*
+ * 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.common.utils;
+
+import org.apache.dubbo.common.extension.Adaptive;
+import org.apache.dubbo.config.annotation.Service;
+
+import org.junit.jupiter.api.Test;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.LinkedList;
+import java.util.List;
+
+import static java.util.Arrays.asList;
+import static org.apache.dubbo.common.utils.AnnotationUtils.excludedType;
+import static org.apache.dubbo.common.utils.AnnotationUtils.findAnnotation;
+import static org.apache.dubbo.common.utils.AnnotationUtils.findMetaAnnotation;
+import static org.apache.dubbo.common.utils.AnnotationUtils.findMetaAnnotations;
+import static org.apache.dubbo.common.utils.AnnotationUtils.getAllDeclaredAnnotations;
+import static org.apache.dubbo.common.utils.AnnotationUtils.getAllMetaAnnotations;
+import static org.apache.dubbo.common.utils.AnnotationUtils.getAnnotation;
+import static org.apache.dubbo.common.utils.AnnotationUtils.getAttribute;
+import static org.apache.dubbo.common.utils.AnnotationUtils.getDeclaredAnnotations;
+import static org.apache.dubbo.common.utils.AnnotationUtils.getMetaAnnotations;
+import static org.apache.dubbo.common.utils.AnnotationUtils.getValue;
+import static org.apache.dubbo.common.utils.AnnotationUtils.isAnnotationPresent;
+import static org.apache.dubbo.common.utils.AnnotationUtils.isAnyAnnotationPresent;
+import static org.apache.dubbo.common.utils.AnnotationUtils.isSameType;
+import static org.apache.dubbo.common.utils.AnnotationUtils.isType;
+import static org.apache.dubbo.common.utils.MethodUtils.findMethod;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link AnnotationUtils} Test
+ *
+ * @since 2.7.6
+ */
+public class AnnotationUtilsTest {
+
+ @Test
+ public void testIsType() throws NoSuchMethodException {
+ // null checking
+ assertFalse(isType(null));
+ // Method checking
+ assertFalse(isType(findMethod(A.class, "execute")));
+ // Class checking
+ assertTrue(isType(A.class));
+ }
+
+ @Test
+ public void testIsSameType() {
+ assertTrue(isSameType(A.class.getAnnotation(Service.class), Service.class));
+ assertFalse(isSameType(A.class.getAnnotation(Service.class), Deprecated.class));
+ assertFalse(isSameType(A.class.getAnnotation(Service.class), null));
+ assertFalse(isSameType(null, Deprecated.class));
+ assertFalse(isSameType(null, null));
+ }
+
+ @Test
+ public void testExcludedType() {
+ assertFalse(excludedType(Service.class).test(A.class.getAnnotation(Service.class)));
+ assertTrue(excludedType(Service.class).test(A.class.getAnnotation(Deprecated.class)));
+ }
+
+ @Test
+ public void testGetAttribute() {
+ Annotation annotation = A.class.getAnnotation(Service.class);
+ assertEquals("java.lang.CharSequence", getAttribute(annotation, "interfaceName"));
+ assertEquals(CharSequence.class, getAttribute(annotation, "interfaceClass"));
+ assertEquals("", getAttribute(annotation, "version"));
+ assertEquals("", getAttribute(annotation, "group"));
+ assertEquals("", getAttribute(annotation, "path"));
+ assertEquals(true, getAttribute(annotation, "export"));
+ assertEquals(false, getAttribute(annotation, "deprecated"));
+ }
+
+ @Test
+ public void testGetValue() {
+ Adaptive adaptive = A.class.getAnnotation(Adaptive.class);
+ String[] value = getValue(adaptive);
+ assertEquals(asList("a", "b", "c"), asList(value));
+ }
+
+ @Test
+ public void testGetDeclaredAnnotations() {
+ List<Annotation> annotations = getDeclaredAnnotations(A.class);
+ assertADeclaredAnnotations(annotations, 0);
+
+ annotations = getDeclaredAnnotations(A.class, a -> isSameType(a, Service.class));
+ assertEquals(1, annotations.size());
+ Service service = (Service) annotations.get(0);
+ assertEquals("java.lang.CharSequence", service.interfaceName());
+ assertEquals(CharSequence.class, service.interfaceClass());
+ }
+
+ @Test
+ public void testGetAllDeclaredAnnotations() {
+ List<Annotation> annotations = getAllDeclaredAnnotations(A.class);
+ assertADeclaredAnnotations(annotations, 0);
+
+ annotations = getAllDeclaredAnnotations(B.class);
+ assertTrue(isSameType(annotations.get(0), Service5.class));
+ assertADeclaredAnnotations(annotations, 1);
+
+ annotations = new LinkedList<>(getAllDeclaredAnnotations(C.class));
+ assertTrue(isSameType(annotations.get(0), MyAdaptive.class));
+ assertTrue(isSameType(annotations.get(1), Service5.class));
+ assertADeclaredAnnotations(annotations, 2);
+
+ annotations = getAllDeclaredAnnotations(findMethod(A.class, "execute"));
+ MyAdaptive myAdaptive = (MyAdaptive) annotations.get(0);
+ assertArrayEquals(new String[]{"e"}, myAdaptive.value());
+
+ annotations = getAllDeclaredAnnotations(findMethod(B.class, "execute"));
+ Adaptive adaptive = (Adaptive) annotations.get(0);
+ assertArrayEquals(new String[]{"f"}, adaptive.value());
+ }
+
+ @Test
+ public void testGetMetaAnnotations() {
+ List<Annotation> metaAnnotations = getMetaAnnotations(Service.class, a -> isSameType(a, Inherited.class));
+ assertEquals(1, metaAnnotations.size());
+ assertEquals(Inherited.class, metaAnnotations.get(0).annotationType());
+
+ metaAnnotations = getMetaAnnotations(Service.class);
+ assertEquals(1, metaAnnotations.size());
+ assertEquals(Inherited.class, metaAnnotations.get(0).annotationType());
+ }
+
+ @Test
+ public void testGetAllMetaAnnotations() {
+ List<Annotation> metaAnnotations = getAllMetaAnnotations(Service5.class);
+ int offset = 0;
+ assertEquals(9, metaAnnotations.size());
+ assertEquals(Inherited.class, metaAnnotations.get(offset++).annotationType());
+ assertEquals(Service4.class, metaAnnotations.get(offset++).annotationType());
+ assertEquals(Inherited.class, metaAnnotations.get(offset++).annotationType());
+ assertEquals(Service3.class, metaAnnotations.get(offset++).annotationType());
+ assertEquals(Inherited.class, metaAnnotations.get(offset++).annotationType());
+ assertEquals(Service2.class, metaAnnotations.get(offset++).annotationType());
+ assertEquals(Inherited.class, metaAnnotations.get(offset++).annotationType());
+ assertEquals(Service.class, metaAnnotations.get(offset++).annotationType());
+ assertEquals(Inherited.class, metaAnnotations.get(offset++).annotationType());
+
+ metaAnnotations = getAllMetaAnnotations(MyAdaptive.class);
+ offset = 0;
+ assertEquals(2, metaAnnotations.size());
+ assertEquals(Inherited.class, metaAnnotations.get(offset++).annotationType());
+ assertEquals(Adaptive.class, metaAnnotations.get(offset++).annotationType());
+ }
+
+
+ @Test
+ public void testIsAnnotationPresent() {
+ assertTrue(isAnnotationPresent(A.class, true, Service.class));
+ assertTrue(isAnnotationPresent(A.class, true, Service.class, com.alibaba.dubbo.config.annotation.Service.class));
+ assertTrue(isAnnotationPresent(A.class, Service.class));
+ assertTrue(isAnnotationPresent(A.class, "org.apache.dubbo.config.annotation.Service"));
+ assertTrue(AnnotationUtils.isAllAnnotationPresent(A.class, Service.class, Service.class, com.alibaba.dubbo.config.annotation.Service.class));
+ assertTrue(isAnnotationPresent(A.class, Deprecated.class));
+ }
+
+ @Test
+ public void testIsAnyAnnotationPresent() {
+ assertTrue(isAnyAnnotationPresent(A.class, Service.class, com.alibaba.dubbo.config.annotation.Service.class, Deprecated.class));
+ assertTrue(isAnyAnnotationPresent(A.class, Service.class, com.alibaba.dubbo.config.annotation.Service.class));
+ assertTrue(isAnyAnnotationPresent(A.class, Service.class, Deprecated.class));
+ assertTrue(isAnyAnnotationPresent(A.class, com.alibaba.dubbo.config.annotation.Service.class, Deprecated.class));
+ assertTrue(isAnyAnnotationPresent(A.class, Service.class));
+ assertTrue(isAnyAnnotationPresent(A.class, com.alibaba.dubbo.config.annotation.Service.class));
+ assertTrue(isAnyAnnotationPresent(A.class, Deprecated.class));
+ }
+
+ @Test
+ public void testGetAnnotation() {
+ assertNotNull(getAnnotation(A.class, "org.apache.dubbo.config.annotation.Service"));
+ assertNotNull(getAnnotation(A.class, "com.alibaba.dubbo.config.annotation.Service"));
+ assertNotNull(getAnnotation(A.class, "org.apache.dubbo.common.extension.Adaptive"));
+ assertNull(getAnnotation(A.class, "java.lang.Deprecated"));
+ assertNull(getAnnotation(A.class, "java.lang.String"));
+ assertNull(getAnnotation(A.class, "NotExistedClass"));
+ }
+
+ @Test
+ public void testFindAnnotation() {
+ Service service = findAnnotation(A.class, Service.class);
+ assertEquals("java.lang.CharSequence", service.interfaceName());
+ assertEquals(CharSequence.class, service.interfaceClass());
+
+ service = findAnnotation(B.class, Service.class);
+ assertEquals(CharSequence.class, service.interfaceClass());
+ }
+
+ @Test
+ public void testFindMetaAnnotations() {
+ List<Service> services = findMetaAnnotations(B.class, Service.class);
+ assertEquals(1, services.size());
+
+ Service service = services.get(0);
+ assertEquals("", service.interfaceName());
+ assertEquals(Cloneable.class, service.interfaceClass());
+
+ services = findMetaAnnotations(Service5.class, Service.class);
+ assertEquals(1, services.size());
+
+ service = services.get(0);
+ assertEquals("", service.interfaceName());
+ assertEquals(Cloneable.class, service.interfaceClass());
+ }
+
+ @Test
+ public void testFindMetaAnnotation() {
+ Service service = findMetaAnnotation(B.class, Service.class);
+ assertEquals(Cloneable.class, service.interfaceClass());
+
+ service = findMetaAnnotation(B.class, "org.apache.dubbo.config.annotation.Service");
+ assertEquals(Cloneable.class, service.interfaceClass());
+
+ service = findMetaAnnotation(Service5.class, Service.class);
+ assertEquals(Cloneable.class, service.interfaceClass());
+ }
+
+ @Service(interfaceName = "java.lang.CharSequence", interfaceClass = CharSequence.class)
+ @com.alibaba.dubbo.config.annotation.Service(interfaceName = "java.lang.CharSequence", interfaceClass = CharSequence.class)
+ @Adaptive(value = {"a", "b", "c"})
+ static class A {
+
+ @MyAdaptive("e")
+ public void execute() {
+
+ }
+
+
+ }
+
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.TYPE})
+ @Inherited
+ @Service(interfaceClass = Cloneable.class)
+ @interface Service2 {
+
+
+ }
+
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.TYPE})
+ @Inherited
+ @Service2
+ @interface Service3 {
+
+
+ }
+
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.TYPE})
+ @Inherited
+ @Service3
+ @interface Service4 {
+
+
+ }
+
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.TYPE})
+ @Inherited
+ @Service4
+ @interface Service5 {
+
+
+ }
+
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.TYPE, ElementType.METHOD})
+ @Inherited
+ @Adaptive
+ @interface MyAdaptive {
+
+ String[] value() default {};
+
+ }
+
+ @Service5
+ static class B extends A {
+
+ @Adaptive("f")
+ @Override
+ public void execute() {
+
+ }
+
+
+ }
+
+ @MyAdaptive
+ static class C extends B {
+
+ }
+
+ private void assertADeclaredAnnotations(List<Annotation> annotations, int offset) {
+ int size = 3 + offset;
+ assertEquals(size, annotations.size());
+ Service service = (Service) annotations.get(offset++);
+ assertEquals("java.lang.CharSequence", service.interfaceName());
+ assertEquals(CharSequence.class, service.interfaceClass());
+
+ com.alibaba.dubbo.config.annotation.Service s = (com.alibaba.dubbo.config.annotation.Service) annotations.get(offset++);
+ assertEquals("java.lang.CharSequence", service.interfaceName());
+ assertEquals(CharSequence.class, service.interfaceClass());
+
+ Adaptive adaptive = (Adaptive) annotations.get(offset++);
+ assertArrayEquals(new String[]{"a", "b", "c"}, adaptive.value());
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MemberUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MemberUtilsTest.java
new file mode 100644
index 0000000..051363b
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MemberUtilsTest.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.common.utils;
+
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.utils.MemberUtils.isStatic;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link MemberUtils} Test
+ *
+ * @since 2.7.6
+ */
+public class MemberUtilsTest {
+
+ @Test
+ public void testIsStatic() throws NoSuchMethodException {
+
+ assertFalse(isStatic(getClass().getMethod("testIsStatic")));
+ assertTrue(isStatic(getClass().getMethod("staticMethod")));
+ }
+
+ public static void staticMethod() {
+
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToBooleanConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToBooleanConverterTest.java
new file mode 100644
index 0000000..3b1d75b
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToBooleanConverterTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.convert;
+
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.StringToBooleanConverter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToBooleanConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToBooleanConverterTest {
+
+ private StringToBooleanConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = (StringToBooleanConverter) getExtensionLoader(Converter.class).getExtension("string-to-boolean");
+ }
+
+ @Test
+ public void testAccept() {
+ assertTrue(converter.accept(String.class, Boolean.class));
+ }
+
+ @Test
+ public void testConvert() {
+ assertTrue(converter.convert("true"));
+ assertTrue(converter.convert("true"));
+ assertTrue(converter.convert("True"));
+ assertFalse(converter.convert("a"));
+ assertNull(converter.convert(""));
+ assertNull(converter.convert(null));
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToCharArrayConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToCharArrayConverterTest.java
new file mode 100644
index 0000000..492a129
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToCharArrayConverterTest.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.convert;
+
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.StringToCharArrayConverter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToCharArrayConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToCharArrayConverterTest {
+
+ private StringToCharArrayConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = (StringToCharArrayConverter) getExtensionLoader(Converter.class).getExtension("string-to-char-array");
+ }
+
+ @Test
+ public void testAccept() {
+ assertTrue(converter.accept(String.class, char[].class));
+ }
+
+ @Test
+ public void testConvert() {
+ assertArrayEquals(new char[]{'1', '2', '3'}, converter.convert("123"));
+ assertNull(converter.convert(null));
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToCharacterConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToCharacterConverterTest.java
new file mode 100644
index 0000000..c9e88c2
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToCharacterConverterTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.convert;
+
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.StringToCharacterConverter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToCharacterConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToCharacterConverterTest {
+
+ private StringToCharacterConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = (StringToCharacterConverter) getExtensionLoader(Converter.class).getExtension("string-to-character");
+ }
+
+ @Test
+ public void testAccept() {
+ assertTrue(converter.accept(String.class, Character.class));
+ }
+
+ @Test
+ public void testConvert() {
+ assertEquals('t', converter.convert("t"));
+ assertNull(converter.convert(null));
+ assertThrows(IllegalArgumentException.class, () -> {
+ converter.convert("ttt");
+ });
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToDoubleConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToDoubleConverterTest.java
new file mode 100644
index 0000000..668f3e6
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToDoubleConverterTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.convert;
+
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.StringToDoubleConverter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToDoubleConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToDoubleConverterTest {
+
+ private StringToDoubleConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = (StringToDoubleConverter) getExtensionLoader(Converter.class).getExtension("string-to-double");
+ }
+
+ @Test
+ public void testAccept() {
+ assertTrue(converter.accept(String.class, Double.class));
+ }
+
+ @Test
+ public void testConvert() {
+ assertEquals(Double.valueOf("1.0"), converter.convert("1.0"));
+ assertNull(converter.convert(null));
+ assertThrows(NumberFormatException.class, () -> {
+ converter.convert("ttt");
+ });
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToFloatConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToFloatConverterTest.java
new file mode 100644
index 0000000..aa17499
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToFloatConverterTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.convert;
+
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.StringToFloatConverter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToFloatConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToFloatConverterTest {
+
+ private StringToFloatConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = (StringToFloatConverter) getExtensionLoader(Converter.class).getExtension("string-to-float");
+ }
+
+ @Test
+ public void testAccept() {
+ assertTrue(converter.accept(String.class, Float.class));
+ }
+
+ @Test
+ public void testConvert() {
+ assertEquals(Float.valueOf("1.0"), converter.convert("1.0"));
+ assertNull(converter.convert(null));
+ assertThrows(NumberFormatException.class, () -> {
+ converter.convert("ttt");
+ });
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToIntegerConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToIntegerConverterTest.java
new file mode 100644
index 0000000..9c7d24b
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToIntegerConverterTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.convert;
+
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.StringToIntegerConverter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToIntegerConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToIntegerConverterTest {
+
+ private StringToIntegerConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = (StringToIntegerConverter) getExtensionLoader(Converter.class).getExtension("string-to-integer");
+ }
+
+ @Test
+ public void testAccept() {
+ assertTrue(converter.accept(String.class, Integer.class));
+ }
+
+ @Test
+ public void testConvert() {
+ assertEquals(Integer.valueOf("1"), converter.convert("1"));
+ assertNull(converter.convert(null));
+ assertThrows(NumberFormatException.class, () -> {
+ converter.convert("ttt");
+ });
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToLongConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToLongConverterTest.java
new file mode 100644
index 0000000..e14424a
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToLongConverterTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.convert;
+
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.StringToLongConverter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToLongConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToLongConverterTest {
+
+ private StringToLongConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = (StringToLongConverter) getExtensionLoader(Converter.class).getExtension("string-to-long");
+ }
+
+ @Test
+ public void testAccept() {
+ assertTrue(converter.accept(String.class, Long.class));
+ }
+
+ @Test
+ public void testConvert() {
+ assertEquals(Long.valueOf("1"), converter.convert("1"));
+ assertNull(converter.convert(null));
+ assertThrows(NumberFormatException.class, () -> {
+ converter.convert("ttt");
+ });
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToOptionalConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToOptionalConverterTest.java
new file mode 100644
index 0000000..242ae60
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToOptionalConverterTest.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.convert;
+
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.StringToOptionalConverter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Optional;
+
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToOptionalConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToOptionalConverterTest {
+
+ private StringToOptionalConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = (StringToOptionalConverter) getExtensionLoader(Converter.class).getExtension("string-to-optional");
+ }
+
+ @Test
+ public void testAccept() {
+ assertTrue(converter.accept(String.class, Optional.class));
+ }
+
+ @Test
+ public void testConvert() {
+ assertEquals(Optional.of("1"), converter.convert("1"));
+ assertEquals(Optional.empty(), converter.convert(null));
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToShortConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToShortConverterTest.java
new file mode 100644
index 0000000..3f1d493
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToShortConverterTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.convert;
+
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.StringToShortConverter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToShortConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToShortConverterTest {
+
+ private StringToShortConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = (StringToShortConverter) getExtensionLoader(Converter.class).getExtension("string-to-short");
+ }
+
+ @Test
+ public void testAccept() {
+ assertTrue(converter.accept(String.class, Short.class));
+ }
+
+ @Test
+ public void testConvert() {
+ assertEquals(Short.valueOf("1"), converter.convert("1"));
+ assertNull(converter.convert(null));
+ assertThrows(NumberFormatException.class, () -> {
+ converter.convert("ttt");
+ });
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToStringConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToStringConverterTest.java
new file mode 100644
index 0000000..57806c3
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/StringToStringConverterTest.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.convert;
+
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.StringToStringConverter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToStringConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToStringConverterTest {
+
+ private StringToStringConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = (StringToStringConverter) getExtensionLoader(Converter.class).getExtension("string-to-string");
+ }
+
+ @Test
+ public void testAccept() {
+ assertTrue(converter.accept(String.class, String.class));
+ }
+
+ @Test
+ public void testConvert() {
+ assertEquals("1", converter.convert("1"));
+ assertNull(converter.convert(null));
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToArrayConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToArrayConverterTest.java
new file mode 100644
index 0000000..1781356
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToArrayConverterTest.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.convert.multiple;
+
+import org.apache.dubbo.common.convert.multiple.StringToArrayConverter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static java.util.Objects.deepEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToArrayConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToArrayConverterTest {
+
+ private StringToArrayConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = new StringToArrayConverter();
+ }
+
+ @Test
+ public void testAccept() {
+ assertTrue(converter.accept(String.class, char[].class));
+ assertTrue(converter.accept(null, char[].class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, null));
+ }
+
+ @Test
+ public void testConvert() {
+ assertTrue(deepEquals(new Integer[]{123}, converter.convert("123", Integer[].class, Integer.class)));
+ assertTrue(deepEquals(new Integer[]{1, 2, 3}, converter.convert("1,2,3", Integer[].class, null)));
+ assertNull(converter.convert("", Integer[].class, null));
+ assertNull(converter.convert(null, Integer[].class, null));
+ }
+
+ @Test
+ public void testGetSourceType() {
+ assertEquals(String.class, converter.getSourceType());
+ }
+
+ @Test
+ public void testGetPriority() {
+ assertEquals(Integer.MAX_VALUE, converter.getPriority());
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToBlockingDequeConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToBlockingDequeConverterTest.java
new file mode 100644
index 0000000..6f9597d
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToBlockingDequeConverterTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.convert.multiple;
+
+import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
+import org.apache.dubbo.common.convert.multiple.StringToBlockingDequeConverter;
+import org.apache.dubbo.common.utils.CollectionUtils;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.TransferQueue;
+
+import static java.util.Arrays.asList;
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToBlockingDequeConverter} Test
+ *
+ * @see BlockingDeque
+ * @since 2.7.6
+ */
+public class StringToBlockingDequeConverterTest {
+
+ private MultiValueConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-blocking-deque");
+ }
+
+ @Test
+ public void testAccept() {
+
+ assertFalse(converter.accept(String.class, Collection.class));
+
+ assertFalse(converter.accept(String.class, List.class));
+ assertFalse(converter.accept(String.class, AbstractList.class));
+ assertFalse(converter.accept(String.class, ArrayList.class));
+ assertFalse(converter.accept(String.class, LinkedList.class));
+
+ assertFalse(converter.accept(String.class, Set.class));
+ assertFalse(converter.accept(String.class, SortedSet.class));
+ assertFalse(converter.accept(String.class, NavigableSet.class));
+ assertFalse(converter.accept(String.class, TreeSet.class));
+ assertFalse(converter.accept(String.class, ConcurrentSkipListSet.class));
+
+ assertFalse(converter.accept(String.class, Queue.class));
+ assertFalse(converter.accept(String.class, BlockingQueue.class));
+ assertFalse(converter.accept(String.class, TransferQueue.class));
+ assertFalse(converter.accept(String.class, Deque.class));
+ assertTrue(converter.accept(String.class, BlockingDeque.class));
+
+ assertFalse(converter.accept(null, char[].class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, null));
+ }
+
+ @Test
+ public void testConvert() throws NoSuchFieldException {
+
+ BlockingQueue<Integer> values = new LinkedBlockingDeque(asList(1, 2, 3));
+
+ BlockingDeque<Integer> result = (BlockingDeque<Integer>) converter.convert("1,2,3", BlockingDeque.class, Integer.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ values = new LinkedBlockingDeque(asList(123));
+
+ result = (BlockingDeque<Integer>) converter.convert("123", BlockingDeque.class, Integer.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ assertNull(converter.convert(null, Collection.class, null));
+ assertNull(converter.convert("", Collection.class, null));
+
+ }
+
+ @Test
+ public void testGetSourceType() {
+ assertEquals(String.class, converter.getSourceType());
+ }
+
+ @Test
+ public void testGetPriority() {
+ assertEquals(Integer.MAX_VALUE - 5, converter.getPriority());
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToBlockingQueueConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToBlockingQueueConverterTest.java
new file mode 100644
index 0000000..4fa7532
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToBlockingQueueConverterTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.convert.multiple;
+
+import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
+import org.apache.dubbo.common.convert.multiple.StringToBlockingQueueConverter;
+import org.apache.dubbo.common.utils.CollectionUtils;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.TransferQueue;
+
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToBlockingQueueConverter} Test
+ *
+ * @see BlockingDeque
+ * @since 2.7.6
+ */
+public class StringToBlockingQueueConverterTest {
+
+ private MultiValueConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-blocking-queue");
+ }
+
+ @Test
+ public void testAccept() {
+
+ assertFalse(converter.accept(String.class, Collection.class));
+
+ assertFalse(converter.accept(String.class, List.class));
+ assertFalse(converter.accept(String.class, AbstractList.class));
+ assertFalse(converter.accept(String.class, ArrayList.class));
+ assertFalse(converter.accept(String.class, LinkedList.class));
+
+ assertFalse(converter.accept(String.class, Set.class));
+ assertFalse(converter.accept(String.class, SortedSet.class));
+ assertFalse(converter.accept(String.class, NavigableSet.class));
+ assertFalse(converter.accept(String.class, TreeSet.class));
+ assertFalse(converter.accept(String.class, ConcurrentSkipListSet.class));
+
+ assertFalse(converter.accept(String.class, Queue.class));
+ assertTrue(converter.accept(String.class, BlockingQueue.class));
+ assertTrue(converter.accept(String.class, TransferQueue.class));
+ assertFalse(converter.accept(String.class, Deque.class));
+ assertTrue(converter.accept(String.class, BlockingDeque.class));
+
+ assertFalse(converter.accept(null, char[].class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, null));
+ }
+
+ @Test
+ public void testConvert() {
+
+ BlockingQueue values = new ArrayBlockingQueue(3);
+ values.offer(1);
+ values.offer(2);
+ values.offer(3);
+
+ BlockingQueue<Integer> result = (BlockingQueue<Integer>) converter.convert("1,2,3", BlockingDeque.class, Integer.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ values.clear();
+ values.offer(123);
+
+ result = (BlockingQueue<Integer>) converter.convert("123", BlockingDeque.class, Integer.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ assertNull(converter.convert(null, Collection.class, null));
+ assertNull(converter.convert("", Collection.class, null));
+
+ }
+
+ @Test
+ public void testGetSourceType() {
+ assertEquals(String.class, converter.getSourceType());
+ }
+
+ @Test
+ public void testGetPriority() {
+ assertEquals(Integer.MAX_VALUE - 3, converter.getPriority());
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToCollectionConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToCollectionConverterTest.java
new file mode 100644
index 0000000..f0b06ec
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToCollectionConverterTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.convert.multiple;
+
+import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
+import org.apache.dubbo.common.convert.multiple.StringToCollectionConverter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.TransferQueue;
+
+import static java.util.Arrays.asList;
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToCollectionConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToCollectionConverterTest {
+
+ private MultiValueConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-collection");
+ }
+
+ @Test
+ public void testAccept() {
+
+ assertTrue(converter.accept(String.class, Collection.class));
+
+ assertTrue(converter.accept(String.class, List.class));
+ assertTrue(converter.accept(String.class, AbstractList.class));
+ assertTrue(converter.accept(String.class, ArrayList.class));
+ assertTrue(converter.accept(String.class, LinkedList.class));
+
+ assertTrue(converter.accept(String.class, Set.class));
+ assertTrue(converter.accept(String.class, SortedSet.class));
+ assertTrue(converter.accept(String.class, NavigableSet.class));
+ assertTrue(converter.accept(String.class, TreeSet.class));
+ assertTrue(converter.accept(String.class, ConcurrentSkipListSet.class));
+
+ assertTrue(converter.accept(String.class, Queue.class));
+ assertTrue(converter.accept(String.class, BlockingQueue.class));
+ assertTrue(converter.accept(String.class, TransferQueue.class));
+ assertTrue(converter.accept(String.class, Deque.class));
+ assertTrue(converter.accept(String.class, BlockingDeque.class));
+
+ assertFalse(converter.accept(null, char[].class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, null));
+ }
+
+ @Test
+ public void testConvert() {
+
+ List values = asList(1L, 2L, 3L);
+
+ Collection result = (Collection<Long>) converter.convert("1,2,3", Collection.class, Long.class);
+
+ assertEquals(values, result);
+
+ values = asList(123);
+
+ result = (Collection<Integer>) converter.convert("123", Collection.class, Integer.class);
+
+ assertEquals(values, result);
+
+ assertNull(converter.convert(null, Collection.class, Integer.class));
+ assertNull(converter.convert("", Collection.class, Integer.class));
+
+ }
+
+ @Test
+ public void testGetSourceType() {
+ assertEquals(String.class, converter.getSourceType());
+ }
+
+ @Test
+ public void testGetPriority() {
+ assertEquals(Integer.MAX_VALUE - 1, converter.getPriority());
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToDequeConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToDequeConverterTest.java
new file mode 100644
index 0000000..e810092
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToDequeConverterTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.convert.multiple;
+
+import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
+import org.apache.dubbo.common.convert.multiple.StringToDequeConverter;
+import org.apache.dubbo.common.utils.CollectionUtils;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.AbstractList;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.TransferQueue;
+
+import static java.util.Arrays.asList;
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToDequeConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToDequeConverterTest {
+
+ private MultiValueConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-deque");
+ }
+
+ @Test
+ public void testAccept() {
+
+ assertFalse(converter.accept(String.class, Collection.class));
+
+ assertFalse(converter.accept(String.class, List.class));
+ assertFalse(converter.accept(String.class, AbstractList.class));
+ assertTrue(converter.accept(String.class, LinkedList.class));
+ assertFalse(converter.accept(String.class, ArrayList.class));
+
+ assertFalse(converter.accept(String.class, Queue.class));
+ assertFalse(converter.accept(String.class, BlockingQueue.class));
+ assertFalse(converter.accept(String.class, TransferQueue.class));
+ assertTrue(converter.accept(String.class, Deque.class));
+ assertTrue(converter.accept(String.class, BlockingDeque.class));
+
+ assertFalse(converter.accept(String.class, Set.class));
+ assertFalse(converter.accept(String.class, SortedSet.class));
+ assertFalse(converter.accept(String.class, NavigableSet.class));
+ assertFalse(converter.accept(String.class, TreeSet.class));
+ assertFalse(converter.accept(String.class, ConcurrentSkipListSet.class));
+
+ assertFalse(converter.accept(null, char[].class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, null));
+ }
+
+ @Test
+ public void testConvert() {
+
+ Deque values = new ArrayDeque(asList(1, 2, 3));
+
+ Deque result = (Deque) converter.convert("1,2,3", Deque.class, Integer.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ values = new ArrayDeque(asList("123"));
+
+ result = (Deque) converter.convert("123", Deque.class, String.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ assertNull(converter.convert(null, Collection.class, Integer.class));
+ assertNull(converter.convert("", Collection.class, null));
+ }
+
+ @Test
+ public void testGetSourceType() {
+ assertEquals(String.class, converter.getSourceType());
+ }
+
+ @Test
+ public void testGetPriority() {
+ assertEquals(Integer.MAX_VALUE - 3, converter.getPriority());
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToListConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToListConverterTest.java
new file mode 100644
index 0000000..af9ee91
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToListConverterTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.convert.multiple;
+
+import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
+import org.apache.dubbo.common.convert.multiple.StringToListConverter;
+import org.apache.dubbo.common.utils.CollectionUtils;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.TransferQueue;
+
+import static java.util.Arrays.asList;
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToListConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToListConverterTest {
+
+ private MultiValueConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-list");
+ }
+
+ @Test
+ public void testAccept() {
+
+ assertFalse(converter.accept(String.class, Collection.class));
+
+ assertTrue(converter.accept(String.class, List.class));
+ assertTrue(converter.accept(String.class, AbstractList.class));
+ assertTrue(converter.accept(String.class, LinkedList.class));
+ assertTrue(converter.accept(String.class, ArrayList.class));
+
+ assertFalse(converter.accept(String.class, Set.class));
+ assertFalse(converter.accept(String.class, SortedSet.class));
+ assertFalse(converter.accept(String.class, NavigableSet.class));
+ assertFalse(converter.accept(String.class, TreeSet.class));
+ assertFalse(converter.accept(String.class, ConcurrentSkipListSet.class));
+
+ assertFalse(converter.accept(String.class, Queue.class));
+ assertFalse(converter.accept(String.class, BlockingQueue.class));
+ assertFalse(converter.accept(String.class, TransferQueue.class));
+ assertFalse(converter.accept(String.class, Deque.class));
+ assertFalse(converter.accept(String.class, BlockingDeque.class));
+
+ assertFalse(converter.accept(null, char[].class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, null));
+ }
+
+ @Test
+ public void testConvert() {
+
+ List values = asList(1, 2, 3);
+
+ List result = (List<Integer>) converter.convert("1,2,3", List.class, Integer.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ values = asList("123");
+
+ result = (List<String>) converter.convert("123", List.class, String.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ assertNull(converter.convert(null, Collection.class, Integer.class));
+ assertNull(converter.convert("", Collection.class, null));
+ }
+
+ @Test
+ public void testGetSourceType() {
+ assertEquals(String.class, converter.getSourceType());
+ }
+
+ @Test
+ public void testGetPriority() {
+ assertEquals(Integer.MAX_VALUE - 2, converter.getPriority());
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToNavigableSetConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToNavigableSetConverterTest.java
new file mode 100644
index 0000000..face60d
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToNavigableSetConverterTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.convert.multiple;
+
+import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
+import org.apache.dubbo.common.convert.multiple.StringToListConverter;
+import org.apache.dubbo.common.utils.CollectionUtils;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.TransferQueue;
+
+import static java.util.Arrays.asList;
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToListConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToNavigableSetConverterTest {
+
+ private MultiValueConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-navigable-set");
+ }
+
+ @Test
+ public void testAccept() {
+
+ assertFalse(converter.accept(String.class, Collection.class));
+
+ assertFalse(converter.accept(String.class, List.class));
+ assertFalse(converter.accept(String.class, AbstractList.class));
+ assertFalse(converter.accept(String.class, LinkedList.class));
+ assertFalse(converter.accept(String.class, ArrayList.class));
+
+ assertFalse(converter.accept(String.class, Set.class));
+ assertFalse(converter.accept(String.class, SortedSet.class));
+ assertTrue(converter.accept(String.class, NavigableSet.class));
+ assertTrue(converter.accept(String.class, TreeSet.class));
+ assertTrue(converter.accept(String.class, ConcurrentSkipListSet.class));
+
+ assertFalse(converter.accept(String.class, Queue.class));
+ assertFalse(converter.accept(String.class, BlockingQueue.class));
+ assertFalse(converter.accept(String.class, TransferQueue.class));
+ assertFalse(converter.accept(String.class, Deque.class));
+ assertFalse(converter.accept(String.class, BlockingDeque.class));
+
+ assertFalse(converter.accept(null, char[].class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, null));
+ }
+
+ @Test
+ public void testConvert() {
+
+ Set values = new TreeSet(asList(1, 2, 3));
+
+ NavigableSet result = (NavigableSet) converter.convert("1,2,3", List.class, Integer.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ values = new TreeSet(asList("123"));
+
+ result = (NavigableSet) converter.convert("123", NavigableSet.class, String.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ assertNull(converter.convert(null, Collection.class, Integer.class));
+ assertNull(converter.convert("", Collection.class, null));
+ }
+
+ @Test
+ public void testGetSourceType() {
+ assertEquals(String.class, converter.getSourceType());
+ }
+
+ @Test
+ public void testGetPriority() {
+ assertEquals(Integer.MAX_VALUE - 4, converter.getPriority());
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToQueueConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToQueueConverterTest.java
new file mode 100644
index 0000000..539693a
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToQueueConverterTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.convert.multiple;
+
+import org.apache.dubbo.common.convert.multiple.StringToQueueConverter;
+import org.apache.dubbo.common.utils.CollectionUtils;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.AbstractList;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.TransferQueue;
+
+import static java.util.Arrays.asList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToQueueConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToQueueConverterTest {
+
+ private StringToQueueConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = new StringToQueueConverter();
+ }
+
+ @Test
+ public void testAccept() {
+
+ assertFalse(converter.accept(String.class, Collection.class));
+
+ assertFalse(converter.accept(String.class, List.class));
+ assertFalse(converter.accept(String.class, AbstractList.class));
+ assertTrue(converter.accept(String.class, LinkedList.class));
+ assertFalse(converter.accept(String.class, ArrayList.class));
+
+ assertTrue(converter.accept(String.class, Queue.class));
+ assertTrue(converter.accept(String.class, BlockingQueue.class));
+ assertTrue(converter.accept(String.class, TransferQueue.class));
+ assertTrue(converter.accept(String.class, Deque.class));
+ assertTrue(converter.accept(String.class, BlockingDeque.class));
+
+ assertFalse(converter.accept(String.class, Set.class));
+ assertFalse(converter.accept(String.class, SortedSet.class));
+ assertFalse(converter.accept(String.class, NavigableSet.class));
+ assertFalse(converter.accept(String.class, TreeSet.class));
+ assertFalse(converter.accept(String.class, ConcurrentSkipListSet.class));
+
+ assertFalse(converter.accept(null, char[].class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, null));
+ }
+
+ @Test
+ public void testConvert() {
+
+ Queue values = new ArrayDeque(asList(1.0, 2.0, 3.0));
+
+ Queue result = (Queue<Double>) converter.convert("1.0,2.0,3.0", Queue.class, Double.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ values.clear();
+ values.add(123);
+
+ result = (Queue) converter.convert("123", Queue.class, Integer.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ assertNull(converter.convert(null, Collection.class, Integer.class));
+ assertNull(converter.convert("", Collection.class, null));
+ }
+
+ @Test
+ public void testGetSourceType() {
+ assertEquals(String.class, converter.getSourceType());
+ }
+
+ @Test
+ public void testGetPriority() {
+ assertEquals(Integer.MAX_VALUE - 2, converter.getPriority());
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToSetConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToSetConverterTest.java
new file mode 100644
index 0000000..269d709
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToSetConverterTest.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.convert.multiple;
+
+import org.apache.dubbo.common.convert.multiple.StringToSetConverter;
+import org.apache.dubbo.common.utils.CollectionUtils;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.TransferQueue;
+
+import static java.util.Arrays.asList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToSetConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToSetConverterTest {
+
+ private StringToSetConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = new StringToSetConverter();
+ }
+
+ @Test
+ public void testAccept() {
+
+ assertFalse(converter.accept(String.class, Collection.class));
+
+ assertFalse(converter.accept(String.class, List.class));
+ assertFalse(converter.accept(String.class, AbstractList.class));
+ assertFalse(converter.accept(String.class, LinkedList.class));
+ assertFalse(converter.accept(String.class, ArrayList.class));
+
+ assertTrue(converter.accept(String.class, Set.class));
+ assertTrue(converter.accept(String.class, SortedSet.class));
+ assertTrue(converter.accept(String.class, NavigableSet.class));
+ assertTrue(converter.accept(String.class, TreeSet.class));
+ assertTrue(converter.accept(String.class, ConcurrentSkipListSet.class));
+
+ assertFalse(converter.accept(String.class, Queue.class));
+ assertFalse(converter.accept(String.class, BlockingQueue.class));
+ assertFalse(converter.accept(String.class, TransferQueue.class));
+ assertFalse(converter.accept(String.class, Deque.class));
+ assertFalse(converter.accept(String.class, BlockingDeque.class));
+
+ assertFalse(converter.accept(null, char[].class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, null));
+ }
+
+ @Test
+ public void testConvert() {
+ Set values = new HashSet(asList(1.0, 2.0, 3.0));
+
+ Set result = (Set<Double>) converter.convert("1.0,2.0,3.0", Queue.class, Double.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ values.clear();
+ values.add(123);
+
+ result = (Set) converter.convert("123", Queue.class, Integer.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ assertNull(converter.convert(null, Collection.class, Integer.class));
+ assertNull(converter.convert("", Collection.class, null));
+ }
+
+ @Test
+ public void testGetSourceType() {
+ assertEquals(String.class, converter.getSourceType());
+ }
+
+ @Test
+ public void testGetPriority() {
+ assertEquals(Integer.MAX_VALUE - 2, converter.getPriority());
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToSortedSetConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToSortedSetConverterTest.java
new file mode 100644
index 0000000..6af8f9d
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToSortedSetConverterTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.convert.multiple;
+
+import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
+import org.apache.dubbo.common.convert.multiple.StringToListConverter;
+import org.apache.dubbo.common.utils.CollectionUtils;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.TransferQueue;
+
+import static java.util.Arrays.asList;
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToListConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToSortedSetConverterTest {
+
+ private MultiValueConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-sorted-set");
+ }
+
+ @Test
+ public void testAccept() {
+
+ assertFalse(converter.accept(String.class, Collection.class));
+
+ assertFalse(converter.accept(String.class, List.class));
+ assertFalse(converter.accept(String.class, AbstractList.class));
+ assertFalse(converter.accept(String.class, LinkedList.class));
+ assertFalse(converter.accept(String.class, ArrayList.class));
+
+ assertFalse(converter.accept(String.class, Set.class));
+ assertTrue(converter.accept(String.class, SortedSet.class));
+ assertTrue(converter.accept(String.class, NavigableSet.class));
+ assertTrue(converter.accept(String.class, TreeSet.class));
+ assertTrue(converter.accept(String.class, ConcurrentSkipListSet.class));
+
+ assertFalse(converter.accept(String.class, Queue.class));
+ assertFalse(converter.accept(String.class, BlockingQueue.class));
+ assertFalse(converter.accept(String.class, TransferQueue.class));
+ assertFalse(converter.accept(String.class, Deque.class));
+ assertFalse(converter.accept(String.class, BlockingDeque.class));
+
+ assertFalse(converter.accept(null, char[].class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, null));
+ }
+
+ @Test
+ public void testConvert() {
+
+ Set values = new TreeSet(asList(1, 2, 3));
+
+ SortedSet result = (SortedSet) converter.convert("1,2,3", List.class, Integer.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ values = new TreeSet(asList("123"));
+
+ result = (SortedSet) converter.convert("123", NavigableSet.class, String.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ assertNull(converter.convert(null, Collection.class, Integer.class));
+ assertNull(converter.convert("", Collection.class, null));
+ }
+
+ @Test
+ public void testGetSourceType() {
+ assertEquals(String.class, converter.getSourceType());
+ }
+
+ @Test
+ public void testGetPriority() {
+ assertEquals(Integer.MAX_VALUE - 3, converter.getPriority());
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToTransferQueueConverterTest.java b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToTransferQueueConverterTest.java
new file mode 100644
index 0000000..4d8d66b
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/convert/multiple/StringToTransferQueueConverterTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.convert.multiple;
+
+import org.apache.dubbo.common.convert.multiple.MultiValueConverter;
+import org.apache.dubbo.common.convert.multiple.StringToListConverter;
+import org.apache.dubbo.common.utils.CollectionUtils;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.LinkedTransferQueue;
+import java.util.concurrent.TransferQueue;
+
+import static java.util.Arrays.asList;
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link StringToListConverter} Test
+ *
+ * @since 2.7.6
+ */
+public class StringToTransferQueueConverterTest {
+
+ private MultiValueConverter converter;
+
+ @BeforeEach
+ public void init() {
+ converter = getExtensionLoader(MultiValueConverter.class).getExtension("string-to-transfer-queue");
+ }
+
+ @Test
+ public void testAccept() {
+
+ assertFalse(converter.accept(String.class, Collection.class));
+
+ assertFalse(converter.accept(String.class, List.class));
+ assertFalse(converter.accept(String.class, AbstractList.class));
+ assertFalse(converter.accept(String.class, LinkedList.class));
+ assertFalse(converter.accept(String.class, ArrayList.class));
+
+ assertFalse(converter.accept(String.class, Set.class));
+ assertFalse(converter.accept(String.class, SortedSet.class));
+ assertFalse(converter.accept(String.class, NavigableSet.class));
+ assertFalse(converter.accept(String.class, TreeSet.class));
+ assertFalse(converter.accept(String.class, ConcurrentSkipListSet.class));
+
+ assertFalse(converter.accept(String.class, Queue.class));
+ assertFalse(converter.accept(String.class, BlockingQueue.class));
+ assertFalse(converter.accept(String.class, Deque.class));
+ assertFalse(converter.accept(String.class, BlockingDeque.class));
+ assertTrue(converter.accept(String.class, TransferQueue.class));
+
+ assertFalse(converter.accept(null, char[].class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, String.class));
+ assertFalse(converter.accept(null, null));
+ }
+
+ @Test
+ public void testConvert() {
+
+ TransferQueue values = new LinkedTransferQueue(asList(1, 2, 3));
+
+ TransferQueue result = (TransferQueue) converter.convert("1,2,3", List.class, Integer.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ values.clear();
+
+ values.addAll(asList("123"));
+
+ result = (TransferQueue) converter.convert("123", NavigableSet.class, String.class);
+
+ assertTrue(CollectionUtils.equals(values, result));
+
+ assertNull(converter.convert(null, Collection.class, Integer.class));
+ assertNull(converter.convert("", Collection.class, null));
+ }
+
+ @Test
+ public void testGetSourceType() {
+ assertEquals(String.class, converter.getSourceType());
+ }
+
+ @Test
+ public void testGetPriority() {
+ assertEquals(Integer.MAX_VALUE - 4, converter.getPriority());
+ }
+}
diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/generic/GenericServiceTest.java b/dubbo-compatible/src/test/java/org/apache/dubbo/generic/GenericServiceTest.java
index 09d62cc..ec9a08e 100644
--- a/dubbo-compatible/src/test/java/org/apache/dubbo/generic/GenericServiceTest.java
+++ b/dubbo-compatible/src/test/java/org/apache/dubbo/generic/GenericServiceTest.java
@@ -34,7 +34,6 @@ import org.apache.dubbo.service.DemoService;
import org.apache.dubbo.service.DemoServiceImpl;
import com.alibaba.fastjson.JSON;
-
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -184,7 +183,7 @@ public class GenericServiceTest {
}
}
Assertions.assertEquals(topTypeDefinition.getProperties().get("v").getType(), "long");
- Assertions.assertEquals(topTypeDefinition.getProperties().get("maps").getType(), "java.util.Map<java.lang.String, java.lang.String>");
+ Assertions.assertEquals(topTypeDefinition.getProperties().get("maps").getType(), "java.util.Map<java.lang.String,java.lang.String>");
Assertions.assertEquals(topTypeDefinition.getProperties().get("innerObject").getType(), "org.apache.dubbo.service.ComplexObject$InnerObject");
Assertions.assertEquals(topTypeDefinition.getProperties().get("intList").getType(), "java.util.List<java.lang.Integer>");
Assertions.assertEquals(topTypeDefinition.getProperties().get("strArrays").getType(), "java.lang.String[]");
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java
index bc63e4d..f826f85 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java
@@ -38,8 +38,7 @@ import org.springframework.context.ApplicationEventPublisherAware;
* @export
*/
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
- ApplicationContextAware, BeanNameAware,
- ApplicationEventPublisherAware {
+ ApplicationContextAware, BeanNameAware, ApplicationEventPublisherAware {
private static final long serialVersionUID = 213195494150089726L;
diff --git a/dubbo-metadata/dubbo-metadata-api/pom.xml b/dubbo-metadata/dubbo-metadata-api/pom.xml
index 8c9a80c..fe6a20c 100644
--- a/dubbo-metadata/dubbo-metadata-api/pom.xml
+++ b/dubbo-metadata/dubbo-metadata-api/pom.xml
@@ -64,6 +64,20 @@
<scope>test</scope>
</dependency>
+ <!-- JAX-RS API -->
+ <dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>javax.ws.rs-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- Spring Web MVC -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ <scope>test</scope>
+ </dependency>
+
</dependencies>
</project>
\ No newline at end of file
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/MethodDefinitionBuilder.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/MethodDefinitionBuilder.java
new file mode 100644
index 0000000..e7c18e6
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/MethodDefinitionBuilder.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.definition;
+
+import org.apache.dubbo.metadata.definition.model.MethodDefinition;
+import org.apache.dubbo.metadata.definition.model.TypeDefinition;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link MethodDefinition} Builder based on Java Reflection
+ *
+ * @since 2.7.6
+ */
+public class MethodDefinitionBuilder {
+
+ private final TypeDefinitionBuilder builder;
+
+ public MethodDefinitionBuilder(TypeDefinitionBuilder builder) {
+ this.builder = builder;
+ }
+
+ public MethodDefinitionBuilder() {
+ this.builder = new TypeDefinitionBuilder();
+ }
+
+ /**
+ * Build the instance of {@link MethodDefinition}
+ *
+ * @param method {@link Method}
+ * @return non-null
+ */
+ public MethodDefinition build(Method method) {
+
+ MethodDefinition md = new MethodDefinition();
+ md.setName(method.getName());
+
+ // Process the parameters
+ Class<?>[] paramTypes = method.getParameterTypes();
+ Type[] genericParamTypes = method.getGenericParameterTypes();
+
+ int paramSize = paramTypes.length;
+ String[] parameterTypes = new String[paramSize];
+ List<TypeDefinition> parameters = new ArrayList<>(paramSize);
+ for (int i = 0; i < paramSize; i++) {
+ TypeDefinition parameter = builder.build(genericParamTypes[i], paramTypes[i]);
+ parameterTypes[i] = parameter.getType();
+ parameters.add(parameter);
+ }
+
+ md.setParameterTypes(parameterTypes);
+ md.setParameters(parameters);
+
+ // Process return type.
+ TypeDefinition td = builder.build(method.getGenericReturnType(), method.getReturnType());
+ md.setReturnType(td.getType());
+
+ return md;
+ }
+
+}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/TypeDefinitionBuilder.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/TypeDefinitionBuilder.java
index b9f68e9..d1275e7 100755
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/TypeDefinitionBuilder.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/TypeDefinitionBuilder.java
@@ -29,6 +29,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import static org.apache.dubbo.common.utils.ClassUtils.isSimpleType;
+
/**
* 2015/1/27.
*/
@@ -55,7 +57,7 @@ public class TypeDefinitionBuilder {
td = DefaultTypeBuilder.build(clazz, typeCache);
td.setTypeBuilderName(DefaultTypeBuilder.class.getName());
}
- if (clazz.equals(String.class)) {
+ if (isSimpleType(clazz)) { // changed since 2.7.6
td.setProperties(null);
}
return td;
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/builder/MapTypeBuilder.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/builder/MapTypeBuilder.java
index a207082..4fd3865 100755
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/builder/MapTypeBuilder.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/builder/MapTypeBuilder.java
@@ -25,6 +25,11 @@ import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Map;
+import static org.apache.dubbo.common.utils.StringUtils.replace;
+import static org.apache.dubbo.common.utils.TypeUtils.getRawClass;
+import static org.apache.dubbo.common.utils.TypeUtils.isClass;
+import static org.apache.dubbo.common.utils.TypeUtils.isParameterizedType;
+
/**
* 2015/1/27.
*/
@@ -46,23 +51,37 @@ public class MapTypeBuilder implements TypeBuilder {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type[] actualTypeArgs = parameterizedType.getActualTypeArguments();
- if (actualTypeArgs == null || actualTypeArgs.length != 2) {
+ int actualTypeArgsLength = actualTypeArgs == null ? 0 : actualTypeArgs.length;
+
+ if (actualTypeArgsLength != 2) {
throw new IllegalArgumentException(MessageFormat.format(
"[ServiceDefinitionBuilder] Map type [{0}] with unexpected amount of arguments [{1}]."
+ Arrays.toString(actualTypeArgs), type, actualTypeArgs));
}
- for (Type actualType : actualTypeArgs) {
- if (actualType instanceof ParameterizedType) {
+ // Change since 2.7.6
+ /**
+ * Replacing <code>", "</code> to <code>","</code> will not change the semantic of
+ * {@link ParameterizedType#toString()}
+ * @see sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
+ */
+ String mapType = replace(type.toString(), ", ", ",");
+
+ TypeDefinition typeDefinition = new TypeDefinition(mapType);
+
+ for (int i = 0; i < actualTypeArgsLength; i++) {
+ Type actualType = actualTypeArgs[i];
+ TypeDefinition item = null;
+ Class<?> rawType = getRawClass(actualType);
+ if (isParameterizedType(actualType)) {
// Nested collection or map.
- Class<?> rawType = (Class<?>) ((ParameterizedType) actualType).getRawType();
- TypeDefinitionBuilder.build(actualType, rawType, typeCache);
- } else if (actualType instanceof Class<?>) {
- Class<?> actualClass = (Class<?>) actualType;
- TypeDefinitionBuilder.build(null, actualClass, typeCache);
+ item = TypeDefinitionBuilder.build(actualType, rawType, typeCache);
+ } else if (isClass(actualType)) {
+ item = TypeDefinitionBuilder.build(null, rawType, typeCache);
}
+ typeDefinition.getItems().add(item);
}
- return new TypeDefinition(type.toString());
+ return typeDefinition;
}
}
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
new file mode 100644
index 0000000..a168f0f
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractAnnotatedMethodParameterProcessor.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 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;
+
+/**
+ * The abstract {@link AnnotatedMethodParameterProcessor} implementation
+ *
+ * @since 2.7.6
+ */
+public abstract class AbstractAnnotatedMethodParameterProcessor implements AnnotatedMethodParameterProcessor {
+
+ @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);
+ process(annotationValue, defaultValue, annotation, parameter, parameterIndex, method, restMethodMetadata);
+ }
+
+ protected String getAnnotationValue(Annotation annotation, Parameter parameter, int parameterIndex) {
+ return getValue(annotation);
+ }
+
+ protected String getDefaultValue(Annotation annotation, Parameter parameter, int parameterIndex) {
+ return 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/AbstractServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java
new file mode 100644
index 0000000..ea07421
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java
@@ -0,0 +1,341 @@
+/*
+ * 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.utils.MethodComparator;
+import org.apache.dubbo.common.utils.ServiceAnnotationResolver;
+import org.apache.dubbo.config.annotation.Service;
+import org.apache.dubbo.metadata.definition.MethodDefinitionBuilder;
+import org.apache.dubbo.metadata.definition.model.MethodDefinition;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.sort;
+import static java.util.Collections.unmodifiableMap;
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.apache.dubbo.common.function.ThrowableFunction.execute;
+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.MethodUtils.excludedDeclaredClass;
+import static org.apache.dubbo.common.utils.MethodUtils.getAllMethods;
+import static org.apache.dubbo.common.utils.MethodUtils.overrides;
+
+/**
+ * The abstract {@link ServiceRestMetadataResolver} class to provider some template methods assemble the instance of
+ * {@link ServiceRestMetadata} will extended by the sub-classes.
+ *
+ * @since 2.7.6
+ */
+public abstract class AbstractServiceRestMetadataResolver implements ServiceRestMetadataResolver {
+
+ private final Map<String, List<AnnotatedMethodParameterProcessor>> parameterProcessorsMap;
+
+ public AbstractServiceRestMetadataResolver() {
+ this.parameterProcessorsMap = loadAnnotatedMethodParameterProcessors();
+ }
+
+ @Override
+ public final boolean supports(Class<?> serviceType) {
+ return isImplementedInterface(serviceType) && isServiceAnnotationPresent(serviceType) && supports0(serviceType);
+ }
+
+ protected final boolean isImplementedInterface(Class<?> serviceType) {
+ return !getAllInterfaces(serviceType).isEmpty();
+ }
+
+ protected final boolean isServiceAnnotationPresent(Class<?> serviceType) {
+ return isAnyAnnotationPresent(serviceType, Service.class, com.alibaba.dubbo.config.annotation.Service.class);
+ }
+
+ /**
+ * internal support method
+ *
+ * @param serviceType Dubbo Service interface or type
+ * @return If supports, return <code>true</code>, or <code>false</code>
+ */
+ protected abstract boolean supports0(Class<?> serviceType);
+
+ @Override
+ public final ServiceRestMetadata resolve(Class<?> serviceType) {
+
+ ServiceRestMetadata serviceRestMetadata = new ServiceRestMetadata();
+
+ // Process ServiceRestMetadata
+ processServiceRestMetadata(serviceRestMetadata, serviceType);
+
+ // Process RestMethodMetadata
+ processAllRestMethodMetadata(serviceRestMetadata, serviceType);
+
+ return serviceRestMetadata;
+ }
+
+ /**
+ * Process the service type including the sub-routines:
+ * <ul>
+ * <li>{@link ServiceRestMetadata#setServiceInterface(String)}</li>
+ * <li>{@link ServiceRestMetadata#setVersion(String)}</li>
+ * <li>{@link ServiceRestMetadata#setGroup(String)}</li>
+ * </ul>
+ *
+ * @param serviceRestMetadata {@link ServiceRestMetadata}
+ * @param serviceType Dubbo Service interface or type
+ */
+ protected void processServiceRestMetadata(ServiceRestMetadata serviceRestMetadata, Class<?> serviceType) {
+ ServiceAnnotationResolver resolver = new ServiceAnnotationResolver(serviceType);
+ serviceRestMetadata.setServiceInterface(resolver.resolveInterfaceClassName());
+ serviceRestMetadata.setVersion(resolver.resolveVersion());
+ serviceRestMetadata.setGroup(resolver.resolveGroup());
+ }
+
+ /**
+ * Process all {@link RestMethodMetadata}
+ *
+ * @param serviceRestMetadata {@link ServiceRestMetadata}
+ * @param serviceType Dubbo Service interface or type
+ */
+ protected void processAllRestMethodMetadata(ServiceRestMetadata serviceRestMetadata, Class<?> serviceType) {
+ Class<?> serviceInterfaceClass = resolveServiceInterfaceClass(serviceRestMetadata, serviceType);
+ Map<Method, Method> serviceMethodsMap = resolveServiceMethodsMap(serviceType, serviceInterfaceClass);
+ for (Map.Entry<Method, Method> entry : serviceMethodsMap.entrySet()) {
+ // 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)) {
+ Method declaredServiceMethod = entry.getValue();
+ processRestMethodMetadata(declaredServiceMethod, serviceType, serviceInterfaceClass,
+ serviceRestMetadata.getMeta()::add);
+ }
+ }
+ }
+
+ /**
+ * Resolve a map of all public services methods from the specified service type and its interface class, whose key is the
+ * declared method, and the value is the overrider method
+ *
+ * @param serviceType the service interface implementation class
+ * @param serviceInterfaceClass the service interface class
+ * @return non-null read-only {@link Map}
+ */
+ protected Map<Method, Method> resolveServiceMethodsMap(Class<?> serviceType, Class<?> serviceInterfaceClass) {
+ Map<Method, Method> serviceMethodsMap = new LinkedHashMap<>();
+ // exclude the public methods declared in java.lang.Object.class
+ List<Method> declaredServiceMethods = new ArrayList<>(getAllMethods(serviceInterfaceClass, excludedDeclaredClass(Object.class)));
+ List<Method> serviceMethods = new ArrayList<>(getAllMethods(serviceType, excludedDeclaredClass(Object.class)));
+
+ // sort methods
+ sort(declaredServiceMethods, MethodComparator.INSTANCE);
+ sort(serviceMethods, MethodComparator.INSTANCE);
+
+ for (Method declaredServiceMethod : declaredServiceMethods) {
+ for (Method serviceMethod : serviceMethods) {
+ if (overrides(serviceMethod, declaredServiceMethod)) {
+ serviceMethodsMap.put(serviceMethod, declaredServiceMethod);
+ continue;
+ }
+ }
+ }
+ // make them to be read-only
+ return unmodifiableMap(serviceMethodsMap);
+ }
+
+ /**
+ * Resolve the class of Dubbo Service interface
+ *
+ * @param serviceRestMetadata {@link ServiceRestMetadata}
+ * @param serviceType Dubbo Service interface or type
+ * @return non-null
+ * @throws RuntimeException If the class is not found, the {@link RuntimeException} wraps the cause will be thrown
+ */
+ protected Class<?> resolveServiceInterfaceClass(ServiceRestMetadata serviceRestMetadata, Class<?> serviceType) {
+ return execute(serviceType.getClassLoader(), classLoader -> {
+ String serviceInterface = serviceRestMetadata.getServiceInterface();
+ return forName(serviceInterface, classLoader);
+ });
+ }
+
+ /**
+ * Process the single {@link RestMethodMetadata} by the specified {@link Consumer} if present
+ *
+ * @param serviceMethod Dubbo Service method
+ * @param serviceType Dubbo Service interface or type
+ * @param serviceInterfaceClass The type of Dubbo Service interface
+ * @param metadataToProcess {@link RestMethodMetadata} to process if present
+ * @return if processed successfully, return <code>true</code>, or <code>false</code>
+ */
+ protected boolean processRestMethodMetadata(Method serviceMethod, Class<?> serviceType,
+ Class<?> serviceInterfaceClass,
+ Consumer<RestMethodMetadata> metadataToProcess) {
+
+ if (!isRestCapableMethod(serviceMethod, serviceType, serviceInterfaceClass)) {
+ return false;
+ }
+
+ String requestPath = resolveRequestPath(serviceMethod, serviceType, serviceInterfaceClass); // requestPath is required
+
+ if (requestPath == null) {
+ return false;
+ }
+
+ String requestMethod = resolveRequestMethod(serviceMethod, serviceType, serviceInterfaceClass); // requestMethod is required
+
+ if (requestMethod == null) {
+ return false;
+ }
+
+ RestMethodMetadata metadata = new RestMethodMetadata();
+
+ MethodDefinition methodDefinition = resolveMethodDefinition(serviceMethod, serviceType, serviceInterfaceClass);
+ // Set MethodDefinition
+ metadata.setMethod(methodDefinition);
+
+ // process the annotated method parameters
+ processAnnotatedMethodParameters(serviceMethod, serviceType, serviceInterfaceClass, metadata);
+
+ // process produces
+ Set<String> produces = new LinkedHashSet<>();
+ processProduces(serviceMethod, serviceType, serviceInterfaceClass, produces);
+
+ // process consumes
+ Set<String> consumes = new LinkedHashSet<>();
+ processConsumes(serviceMethod, serviceType, serviceInterfaceClass, consumes);
+
+ // Initialize RequestMetadata
+ RequestMetadata request = metadata.getRequest();
+ request.setPath(requestPath);
+ request.setMethod(requestMethod);
+ request.setProduces(produces);
+ request.setConsumes(consumes);
+
+ // Post-Process
+ postResolveRestMethodMetadata(serviceMethod, serviceType, serviceInterfaceClass, metadata);
+
+ // Accept RestMethodMetadata
+ metadataToProcess.accept(metadata);
+
+ return true;
+ }
+
+ /**
+ * Test the service method is capable of REST or not?
+ *
+ * @param serviceMethod Dubbo Service method
+ * @param serviceType Dubbo Service interface or type
+ * @param serviceInterfaceClass The type of Dubbo Service interface
+ * @return If capable, return <code>true</code>
+ */
+ protected abstract boolean isRestCapableMethod(Method serviceMethod, Class<?> serviceType, Class<?>
+ serviceInterfaceClass);
+
+ /**
+ * Resolve the request method
+ *
+ * @param serviceMethod Dubbo Service method
+ * @param serviceType Dubbo Service interface or type
+ * @param serviceInterfaceClass The type of Dubbo Service interface
+ * @return if can't be resolve, return <code>null</code>
+ */
+ protected abstract String resolveRequestMethod(Method serviceMethod, Class<?> serviceType, Class<?>
+ serviceInterfaceClass);
+
+ /**
+ * Resolve the request path
+ *
+ * @param serviceMethod Dubbo Service method
+ * @param serviceType Dubbo Service interface or type
+ * @param serviceInterfaceClass The type of Dubbo Service interface
+ * @return if can't be resolve, return <code>null</code>
+ */
+ protected abstract String resolveRequestPath(Method serviceMethod, Class<?> serviceType, Class<?>
+ serviceInterfaceClass);
+
+ /**
+ * Resolve the {@link MethodDefinition}
+ *
+ * @param serviceMethod Dubbo Service method
+ * @param serviceType Dubbo Service interface or type
+ * @param serviceInterfaceClass The type of Dubbo Service interface
+ * @return if can't be resolve, return <code>null</code>
+ * @see MethodDefinitionBuilder
+ */
+ protected MethodDefinition resolveMethodDefinition(Method serviceMethod, Class<?> serviceType,
+ Class<?> serviceInterfaceClass) {
+ MethodDefinitionBuilder builder = new MethodDefinitionBuilder();
+ return builder.build(serviceMethod);
+ }
+
+ private void processAnnotatedMethodParameters(Method serviceMethod, Class<?> serviceType,
+ Class<?> serviceInterfaceClass, RestMethodMetadata metadata) {
+ int paramCount = serviceMethod.getParameterCount();
+ Parameter[] parameters = serviceMethod.getParameters();
+ for (int i = 0; i < paramCount; i++) {
+ Parameter parameter = parameters[i];
+ // Add indexed parameter name
+ metadata.addIndexToName(i,parameter.getName());
+ processAnnotatedMethodParameter(parameter, i, serviceMethod, serviceType, serviceInterfaceClass, metadata);
+ }
+ }
+
+ private void processAnnotatedMethodParameter(Parameter parameter, int parameterIndex, Method serviceMethod,
+ Class<?> serviceType, Class<?> serviceInterfaceClass,
+ RestMethodMetadata metadata) {
+ Annotation[] annotations = parameter.getAnnotations();
+ for (Annotation annotation : annotations) {
+ String annotationType = annotation.annotationType().getName();
+ parameterProcessorsMap.getOrDefault(annotationType, emptyList())
+ .forEach(processor -> {
+ processor.process(annotation, parameter, parameterIndex, serviceMethod, serviceType,
+ serviceInterfaceClass, metadata);
+ });
+ }
+ }
+
+ protected abstract void processProduces(Method serviceMethod, Class<?> serviceType, Class<?>
+ serviceInterfaceClass,
+ Set<String> produces);
+
+ protected abstract void processConsumes(Method serviceMethod, Class<?> serviceType, Class<?>
+ serviceInterfaceClass,
+ Set<String> consumes);
+
+ protected void postResolveRestMethodMetadata(Method serviceMethod, Class<?> serviceType,
+ Class<?> serviceInterfaceClass, RestMethodMetadata metadata) {
+ }
+
+ private static Map<String, List<AnnotatedMethodParameterProcessor>> loadAnnotatedMethodParameterProcessors() {
+ Map<String, List<AnnotatedMethodParameterProcessor>> parameterProcessorsMap = new LinkedHashMap<>();
+ getExtensionLoader(AnnotatedMethodParameterProcessor.class)
+ .getSupportedExtensionInstances()
+ .forEach(processor -> {
+ List<AnnotatedMethodParameterProcessor> processors =
+ parameterProcessorsMap.computeIfAbsent(processor.getAnnotationType(), k -> new LinkedList<>());
+ processors.add(processor);
+ });
+ return parameterProcessorsMap;
+ }
+}
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
new file mode 100644
index 0000000..bd52017
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AnnotatedMethodParameterProcessor.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.metadata.rest;
+
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.common.lang.Prioritized;
+
+import javax.lang.model.element.VariableElement;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+/**
+ * The interface to process the annotated method parameter
+ *
+ * @since 2.7.6
+ */
+@SPI
+public interface AnnotatedMethodParameterProcessor extends Prioritized {
+
+ /**
+ * The string presenting the annotation type
+ *
+ * @return non-null
+ */
+ String getAnnotationType();
+
+ /**
+ * Process the specified method {@link VariableElement parameter}
+ *
+ * @param annotation {@link Annotation the target annotation} whose type is {@link #getAnnotationType()}
+ * @param parameter the method parameter
+ * @param parameterIndex the index of method parameter
+ * @param method {@link Method method that parameter belongs to}
+ * @param serviceType Dubbo Service interface or type
+ * @param serviceInterfaceClass The type of Dubbo Service interface
+ * @param restMethodMetadata {@link RestMethodMetadata the metadata is used to update}
+ */
+ void process(Annotation annotation, Parameter parameter, int parameterIndex, Method method,
+ Class<?> serviceType, Class<?> serviceInterfaceClass, RestMethodMetadata restMethodMetadata);
+
+ /**
+ * Build the default value
+ *
+ * @param parameterIndex the index of parameter
+ * @return the placeholder
+ */
+ static String buildDefaultValue(int parameterIndex) {
+ return "{" + parameterIndex + "}";
+ }
+
+}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ClassPathServiceRestMetadataReader.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ClassPathServiceRestMetadataReader.java
new file mode 100644
index 0000000..bd4d5a0
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ClassPathServiceRestMetadataReader.java
@@ -0,0 +1,82 @@
+/*
+ * 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 com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+
+import static java.util.Collections.unmodifiableList;
+import static org.apache.dubbo.common.function.ThrowableAction.execute;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.METADATA_ENCODING;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SERVICE_REST_METADATA_RESOURCE_PATH;
+
+/**
+ * Class-Path based {@link ServiceRestMetadataReader} implementation
+ *
+ * @see ServiceRestMetadataReader
+ * @since 2.7.6
+ */
+public class ClassPathServiceRestMetadataReader implements ServiceRestMetadataReader {
+
+ private final String serviceRestMetadataJsonResoucePath;
+
+ public ClassPathServiceRestMetadataReader() {
+ this(SERVICE_REST_METADATA_RESOURCE_PATH);
+ }
+
+ public ClassPathServiceRestMetadataReader(String serviceRestMetadataJsonResoucePath) {
+ this.serviceRestMetadataJsonResoucePath = serviceRestMetadataJsonResoucePath;
+ }
+
+ @Override
+ public List<ServiceRestMetadata> read() {
+
+ List<ServiceRestMetadata> serviceRestMetadataList = new LinkedList<>();
+
+ ClassLoader classLoader = getClass().getClassLoader();
+
+ execute(() -> {
+ Enumeration<URL> resources = classLoader.getResources(serviceRestMetadataJsonResoucePath);
+ Gson gson = new Gson();
+ while (resources.hasMoreElements()) {
+ URL resource = resources.nextElement();
+ InputStream inputStream = resource.openStream();
+ JsonParser parser = new JsonParser();
+ JsonElement jsonElement = parser.parse(new InputStreamReader(inputStream, METADATA_ENCODING));
+ if (jsonElement.isJsonArray()) {
+ JsonArray jsonArray = jsonElement.getAsJsonArray();
+ for (int i = 0; i < jsonArray.size(); i++) {
+ JsonElement childJsonElement = jsonArray.get(i);
+ ServiceRestMetadata serviceRestMetadata = gson.fromJson(childJsonElement, ServiceRestMetadata.class);
+ serviceRestMetadataList.add(serviceRestMetadata);
+ }
+ }
+ }
+ });
+
+ return unmodifiableList(serviceRestMetadataList);
+ }
+}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/DefaultServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/DefaultServiceRestMetadataResolver.java
new file mode 100644
index 0000000..ce53dd8
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/DefaultServiceRestMetadataResolver.java
@@ -0,0 +1,59 @@
+/*
+ * 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.Method;
+import java.util.Set;
+
+
+/**
+ * The default implementation {@link ServiceRestMetadataResolver}
+ *
+ * @since 2.7.6
+ */
+public class DefaultServiceRestMetadataResolver extends AbstractServiceRestMetadataResolver {
+
+ @Override
+ protected boolean supports0(Class<?> serviceType) {
+ return false;
+ }
+
+ @Override
+ protected boolean isRestCapableMethod(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+ return false;
+ }
+
+ @Override
+ protected String resolveRequestMethod(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+ return null;
+ }
+
+ @Override
+ protected String resolveRequestPath(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+ return null;
+ }
+
+ @Override
+ protected void processProduces(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, Set<String> produces) {
+
+ }
+
+ @Override
+ protected void processConsumes(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, Set<String> consumes) {
+
+ }
+}
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
new file mode 100644
index 0000000..9381d20
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RequestMetadata.java
@@ -0,0 +1,226 @@
+/*
+ * 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.utils.CollectionUtils;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import static java.util.Collections.unmodifiableMap;
+import static org.apache.dubbo.common.utils.HttpUtils.normalizePath;
+import static org.apache.dubbo.common.utils.StringUtils.isBlank;
+
+/**
+ * The metadata class for REST request
+ *
+ * @since 2.7.6
+ */
+public class RequestMetadata implements Serializable {
+
+ private static final long serialVersionUID = -240099840085329958L;
+
+ private String method;
+
+ private String path;
+
+ private Map<String, List<String>> params = new LinkedHashMap<>();
+
+ private Map<String, List<String>> headers = new LinkedHashMap<>();
+
+ private Set<String> consumes = new LinkedHashSet<>();
+
+ private Set<String> produces = new LinkedHashSet<>();
+
+ /**
+ * Default Constructor
+ */
+ public RequestMetadata() {
+ }
+
+ public String getMethod() {
+ return method;
+ }
+
+ public void setMethod(String method) {
+ this.method = method == null ? null : method.toUpperCase();
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = normalizePath(path);
+ }
+
+ public Map<String, List<String>> getParams() {
+ return unmodifiableMap(params);
+ }
+
+ public void setParams(Map<String, List<String>> params) {
+ params(params);
+ }
+
+ private static void add(Map<String, List<String>> multiValueMap, String key, String value) {
+ if (isBlank(key)) {
+ return;
+ }
+ List<String> values = get(multiValueMap, key, true);
+ values.add(value);
+ }
+
+ private static <T extends Collection<String>> void addAll(Map<String, List<String>> multiValueMap,
+ Map<String, T> source) {
+ for (Map.Entry<String, T> entry : source.entrySet()) {
+ String key = entry.getKey();
+ for (String value : entry.getValue()) {
+ add(multiValueMap, key, value);
+ }
+ }
+ }
+
+ private static String getFirst(Map<String, List<String>> multiValueMap, String key) {
+ List<String> values = get(multiValueMap, key);
+ return CollectionUtils.isNotEmpty(values) ? values.get(0) : null;
+ }
+
+ private static List<String> get(Map<String, List<String>> multiValueMap, String key) {
+ return get(multiValueMap, key, false);
+ }
+
+ private static List<String> get(Map<String, List<String>> multiValueMap, String key, boolean createIfAbsent) {
+ return createIfAbsent ? multiValueMap.computeIfAbsent(key, k -> new LinkedList<>()) : multiValueMap.get(key);
+ }
+
+ public Map<String, List<String>> getHeaders() {
+ return unmodifiableMap(headers);
+ }
+
+ public void setHeaders(Map<String, List<String>> headers) {
+ headers(headers);
+ }
+
+ public Set<String> getConsumes() {
+ return consumes;
+ }
+
+ public void setConsumes(Set<String> consumes) {
+ this.consumes = consumes;
+ }
+
+ public Set<String> getProduces() {
+ return produces;
+ }
+
+ public void setProduces(Set<String> produces) {
+ this.produces = produces;
+ }
+
+ public Set<String> getParamNames() {
+ return params.keySet();
+ }
+
+ public Set<String> getHeaderNames() {
+ return headers.keySet();
+ }
+
+// public List<MediaType> getConsumeMediaTypes() {
+// return toMediaTypes(consumes);
+// }
+//
+// public List<MediaType> getProduceMediaTypes() {
+// return toMediaTypes(produces);
+// }
+
+ public String getParameter(String name) {
+ return getFirst(params, name);
+ }
+
+ public String getHeader(String name) {
+ return getFirst(headers, name);
+ }
+
+ public RequestMetadata addParam(String name, String value) {
+ add(params, name, value);
+ return this;
+ }
+
+ public RequestMetadata addHeader(String name, String value) {
+ add(headers, name, value);
+ return this;
+ }
+
+ private <T extends Collection<String>> RequestMetadata params(Map<String, T> params) {
+ addAll(this.params, params);
+ return this;
+ }
+
+ private <T extends Collection<String>> RequestMetadata headers(Map<String, List<String>> headers) {
+ if (headers != null && !headers.isEmpty()) {
+ Map<String, List<String>> httpHeaders = new LinkedHashMap<>();
+ // Add all headers
+ addAll(headers, httpHeaders);
+ // Handles "Content-Type" and "Accept" headers if present
+// mediaTypes(httpHeaders, HttpHeaders.CONTENT_TYPE, this.consumes);
+// mediaTypes(httpHeaders, HttpHeaders.ACCEPT, this.produces);
+ this.headers.putAll(httpHeaders);
+ }
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof RequestMetadata)) {
+ return false;
+ }
+ 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());
+
+ }
+
+ @Override
+ public int hashCode() {
+ // The values of metadata should not use for the hashCode() method
+ return Objects.hash(method, path, consumes, produces, getParamNames(),
+ getHeaderNames());
+ }
+
+ @Override
+ public String toString() {
+ return "RequestMetadata{" + "method='" + method + '\'' + ", path='" + path + '\''
+ + ", 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
new file mode 100644
index 0000000..82e6328
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java
@@ -0,0 +1,112 @@
+/*
+ * 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;
+
+/**
+ * The REST Metadata Constants definition interface
+ *
+ * @since 2.7.6
+ */
+public interface RestMetadataConstants {
+
+ /**
+ * The encoding of metadata
+ */
+ String METADATA_ENCODING = "UTF-8";
+
+ /**
+ * {@link ServiceRestMetadata} Resource PATH
+ */
+ String SERVICE_REST_METADATA_RESOURCE_PATH = "META-INF/dubbo/service-rest-metadata.json";
+
+ /**
+ * JAX-RS
+ */
+ interface JAX_RS {
+
+ /**
+ * The annotation class name of @Path
+ */
+ String PATH_ANNOTATION_CLASS_NAME = "javax.ws.rs.Path";
+
+ /**
+ * The annotation class name of @HttpMethod
+ */
+ String HTTP_METHOD_ANNOTATION_CLASS_NAME = "javax.ws.rs.HttpMethod";
+
+ /**
+ * The annotation class name of @Produces
+ */
+ String PRODUCES_ANNOTATION_CLASS_NAME = "javax.ws.rs.Produces";
+
+ /**
+ * The annotation class name of @Consumes
+ */
+ String CONSUMES_ANNOTATION_CLASS_NAME = "javax.ws.rs.Consumes";
+
+ /**
+ * The annotation class name of @DefaultValue
+ */
+ String DEFAULT_VALUE_ANNOTATION_CLASS_NAME = "javax.ws.rs.DefaultValue";
+
+ /**
+ * The annotation class name of @FormParam
+ */
+ String FORM_PARAM_ANNOTATION_CLASS_NAME = "javax.ws.rs.FormParam";
+
+ /**
+ * The annotation class name of @HeaderParam
+ */
+ String HEADER_PARAM_ANNOTATION_CLASS_NAME = "javax.ws.rs.HeaderParam";
+
+ /**
+ * The annotation class name of @MatrixParam
+ */
+ String MATRIX_PARAM_ANNOTATION_CLASS_NAME = "javax.ws.rs.MatrixParam";
+
+ /**
+ * The annotation class name of @QueryParam
+ */
+ String QUERY_PARAM_ANNOTATION_CLASS_NAME = "javax.ws.rs.QueryParam";
+ }
+
+ /**
+ * Spring MVC
+ */
+ interface SPRING_MVC {
+
+ /**
+ * The annotation class name of @Controller
+ */
+ String CONTROLLER_ANNOTATION_CLASS_NAME = "org.springframework.stereotype.Controller";
+
+ /**
+ * The annotation class name of @RequestMapping
+ */
+ String REQUEST_MAPPING_ANNOTATION_CLASS_NAME = "org.springframework.web.bind.annotation.RequestMapping";
+
+ /**
+ * The annotation class name of @RequestHeader
+ */
+ String REQUEST_HEADER_ANNOTATION_CLASS_NAME = "org.springframework.web.bind.annotation.RequestHeader";
+
+ /**
+ * The annotation class name of @RequestParam
+ */
+ String REQUEST_PARAM_ANNOTATION_CLASS_NAME = "org.springframework.web.bind.annotation.RequestParam";
+ }
+}
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
new file mode 100644
index 0000000..a675eb3
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java
@@ -0,0 +1,195 @@
+/*
+ * 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.definition.model.MethodDefinition;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static java.util.Collections.emptyList;
+
+/**
+ * The metadata class for {@link RequestMetadata HTTP(REST) request} and
+ * its binding {@link MethodDefinition method definition}
+ *
+ * @since 2.7.6
+ */
+public class RestMethodMetadata implements Serializable {
+
+ private static final long serialVersionUID = 2935252016200830694L;
+
+ private MethodDefinition method;
+
+ private RequestMetadata request;
+
+ private Integer urlIndex;
+
+ private Integer bodyIndex;
+
+ private Integer headerMapIndex;
+
+ private String bodyType;
+
+ private Map<Integer, Collection<String>> indexToName;
+
+ private List<String> formParams;
+
+ private Map<Integer, Boolean> indexToEncoded;
+
+ public MethodDefinition getMethod() {
+ if (method == null) {
+ method = new MethodDefinition();
+ }
+ return method;
+ }
+
+ public void setMethod(MethodDefinition method) {
+ this.method = method;
+ }
+
+ public RequestMetadata getRequest() {
+ if (request == null) {
+ request = new RequestMetadata();
+ }
+ return request;
+ }
+
+ public void setRequest(RequestMetadata request) {
+ this.request = request;
+ }
+
+ public Integer getUrlIndex() {
+ return urlIndex;
+ }
+
+ public void setUrlIndex(Integer urlIndex) {
+ this.urlIndex = urlIndex;
+ }
+
+ public Integer getBodyIndex() {
+ return bodyIndex;
+ }
+
+ public void setBodyIndex(Integer bodyIndex) {
+ this.bodyIndex = bodyIndex;
+ }
+
+ public Integer getHeaderMapIndex() {
+ return headerMapIndex;
+ }
+
+ public void setHeaderMapIndex(Integer headerMapIndex) {
+ this.headerMapIndex = headerMapIndex;
+ }
+
+ public String getBodyType() {
+ return bodyType;
+ }
+
+ public void setBodyType(String bodyType) {
+ this.bodyType = bodyType;
+ }
+
+ public Map<Integer, Collection<String>> getIndexToName() {
+ if (indexToName == null) {
+ indexToName = new HashMap<>();
+ }
+ return indexToName;
+ }
+
+ public void setIndexToName(Map<Integer, Collection<String>> indexToName) {
+ this.indexToName = indexToName;
+ }
+
+ public void addIndexToName(Integer index, String name) {
+ if (index == null) {
+ return;
+ }
+
+ if (name.startsWith("arg") && name.endsWith(index.toString())) {
+ // Ignore this value because of the Java byte-code without the metadata of method parameters
+ return;
+ }
+
+ Map<Integer, Collection<String>> indexToName = getIndexToName();
+ Collection<String> parameterNames = indexToName.computeIfAbsent(index, i -> new ArrayList<>(1));
+ parameterNames.add(name);
+ }
+
+ public boolean hasIndexedName(Integer index, String name) {
+ Map<Integer, Collection<String>> indexToName = getIndexToName();
+ return indexToName.getOrDefault(index, emptyList()).contains(name);
+ }
+
+ public List<String> getFormParams() {
+ return formParams;
+ }
+
+ public void setFormParams(List<String> formParams) {
+ this.formParams = formParams;
+ }
+
+ public Map<Integer, Boolean> getIndexToEncoded() {
+ return indexToEncoded;
+ }
+
+ public void setIndexToEncoded(Map<Integer, Boolean> indexToEncoded) {
+ this.indexToEncoded = indexToEncoded;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof RestMethodMetadata)) return false;
+ 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());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getMethod(), getRequest(), getUrlIndex(), getBodyIndex(), getHeaderMapIndex(), getBodyType(), getFormParams(), getIndexToEncoded());
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("RestMethodMetadata{");
+ sb.append("method=").append(method);
+ sb.append(", request=").append(request);
+ sb.append(", urlIndex=").append(urlIndex);
+ sb.append(", bodyIndex=").append(bodyIndex);
+ sb.append(", headerMapIndex=").append(headerMapIndex);
+ sb.append(", bodyType='").append(bodyType).append('\'');
+ sb.append(", indexToName=").append(indexToName);
+ sb.append(", formParams=").append(formParams);
+ sb.append(", indexToEncoded=").append(indexToEncoded);
+ sb.append('}');
+ return sb.toString();
+ }
+}
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
new file mode 100644
index 0000000..876b8a3
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.rest;
+
+import java.io.Serializable;
+import java.util.LinkedHashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * The metadata class for {@link RequestMetadata HTTP(REST) request} and
+ * its binding Dubbo service metadata
+ *
+ * @since 2.7.6
+ */
+public class ServiceRestMetadata implements Serializable {
+
+ private static final long serialVersionUID = -4549723140727443569L;
+
+ private String serviceInterface;
+
+ private String version;
+
+ private String group;
+
+ private Set<RestMethodMetadata> meta;
+
+ public String getServiceInterface() {
+ return serviceInterface;
+ }
+
+ public void setServiceInterface(String serviceInterface) {
+ this.serviceInterface = serviceInterface;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ this.group = group;
+ }
+
+ public Set<RestMethodMetadata> getMeta() {
+ if (meta == null) {
+ meta = new LinkedHashSet<>();
+ }
+ return meta;
+ }
+
+ public void setMeta(Set<RestMethodMetadata> meta) {
+ this.meta = meta;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ServiceRestMetadata)) return false;
+ 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());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getServiceInterface(), getVersion(), getGroup(), getMeta());
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("ServiceRestMetadata{");
+ sb.append("serviceInterface='").append(serviceInterface).append('\'');
+ sb.append(", version='").append(version).append('\'');
+ sb.append(", group='").append(group).append('\'');
+ sb.append(", meta=").append(meta);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadataReader.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadataReader.java
new file mode 100644
index 0000000..de5207a
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadataReader.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.metadata.rest;
+
+
+import org.apache.dubbo.common.extension.SPI;
+
+import java.util.List;
+
+/**
+ * An interface to read {@link ServiceRestMetadata}
+ *
+ * @see ServiceRestMetadata
+ * @since 2.7.6
+ */
+@SPI
+public interface ServiceRestMetadataReader {
+
+ /**
+ * Read the instances of {@link ServiceRestMetadata}
+ *
+ * @return non-null
+ */
+ List<ServiceRestMetadata> read();
+}
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
new file mode 100644
index 0000000..0c1ece8
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadataResolver.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;
+
+/**
+ * The interface to resolve the {@link ServiceRestMetadata REST metadata} from the specified
+ * Dubbo Service interface or type.
+ *
+ * @since 2.7.6
+ */
+public interface ServiceRestMetadataResolver {
+
+ /**
+ * Support to resolve {@link ServiceRestMetadata REST metadata} or not
+ *
+ * @param serviceType Dubbo Service interface or type
+ * @return If supports, return <code>true</code>, or <code>false</code>
+ */
+ boolean supports(Class<?> serviceType);
+
+ /**
+ * Resolve the {@link ServiceRestMetadata REST metadata} from the specified
+ * Dubbo Service interface or type
+ *
+ * @param serviceType Dubbo Service interface or type
+ * @return
+ */
+ ServiceRestMetadata resolve(Class<?> serviceType);
+}
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
new file mode 100644
index 0000000..a9e7c0e
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/DefaultValueParameterProcessor.java
@@ -0,0 +1,74 @@
+/*
+ * 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.RequestMetadata;
+import org.apache.dubbo.metadata.rest.RestMethodMetadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.DEFAULT_VALUE_ANNOTATION_CLASS_NAME;
+
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @DefaultValue
+ * *
+ *
+ * @since 2.7.6
+ */
+public class DefaultValueParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+ @Override
+ public String getAnnotationType() {
+ return DEFAULT_VALUE_ANNOTATION_CLASS_NAME;
+ }
+
+ @Override
+ protected void process(String annotationValue, String defaultValue, Annotation annotation, Object parameter,
+ int parameterIndex, Method method, RestMethodMetadata restMethodMetadata) {
+ RequestMetadata requestMetadata = restMethodMetadata.getRequest();
+
+ // process the request parameters
+ setDefaultValue(requestMetadata.getParams(), defaultValue, annotationValue);
+ // process the request headers
+ setDefaultValue(requestMetadata.getHeaders(), defaultValue, annotationValue);
+ }
+
+ private void setDefaultValue(Map<String, List<String>> source, String placeholderValue, String defaultValue) {
+ OUTTER:
+ for (Map.Entry<String, List<String>> entry : source.entrySet()) {
+ List<String> values = entry.getValue();
+ int size = values.size();
+ for (int i = 0; i < size; i++) {
+ String value = values.get(i);
+ if (placeholderValue.equals(value)) {
+ values.set(i, defaultValue);
+ break OUTTER;
+ }
+ }
+ }
+ }
+
+ public int getPriority() {
+ return MIN_PRIORITY;
+ }
+}
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
new file mode 100644
index 0000000..d102e39
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/FormParamParameterProcessor.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.metadata.rest.jaxrs;
+
+import org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor;
+
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.FORM_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @FormParam
+ *
+ * @since 2.7.6
+ */
+public class FormParamParameterProcessor extends ParamAnnotationParameterProcessor {
+
+ @Override
+ public String getAnnotationType() {
+ 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
new file mode 100644
index 0000000..16d21bf
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/HeaderParamParameterProcessor.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.rest.jaxrs;
+
+import org.apache.dubbo.metadata.rest.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.rest.RequestMetadata;
+import org.apache.dubbo.metadata.rest.RestMethodMetadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+import static org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor.buildDefaultValue;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.HEADER_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @HeaderParam
+ *
+ * @since 2.7.6
+ */
+public class HeaderParamParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+ @Override
+ public String getAnnotationType() {
+ return HEADER_PARAM_ANNOTATION_CLASS_NAME;
+ }
+
+ @Override
+ protected void process(String headerName, String defaultValue, Annotation annotation, Object parameter,
+ int parameterIndex, Method method, RestMethodMetadata restMethodMetadata) {
+ RequestMetadata requestMetadata = restMethodMetadata.getRequest();
+ // Add the placeholder as header value
+ requestMetadata.addHeader(headerName, buildDefaultValue(parameterIndex));
+ }
+}
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
new file mode 100644
index 0000000..ca4a77f
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolver.java
@@ -0,0 +1,99 @@
+/*
+ * 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.AbstractServiceRestMetadataResolver;
+import org.apache.dubbo.metadata.rest.ServiceRestMetadataResolver;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import static org.apache.dubbo.common.utils.AnnotationUtils.findAnnotation;
+import static org.apache.dubbo.common.utils.AnnotationUtils.findMetaAnnotation;
+import static org.apache.dubbo.common.utils.AnnotationUtils.getValue;
+import static org.apache.dubbo.common.utils.AnnotationUtils.isAnnotationPresent;
+import static org.apache.dubbo.common.utils.HttpUtils.buildPath;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.CONSUMES_ANNOTATION_CLASS_NAME;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.HTTP_METHOD_ANNOTATION_CLASS_NAME;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.PATH_ANNOTATION_CLASS_NAME;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.PRODUCES_ANNOTATION_CLASS_NAME;
+
+/**
+ * JAX-RS {@link ServiceRestMetadataResolver} implementation
+ *
+ * @since 2.7.6
+ */
+public class JAXRSServiceRestMetadataResolver extends AbstractServiceRestMetadataResolver {
+
+ @Override
+ protected boolean supports0(Class<?> serviceType) {
+ return isAnnotationPresent(serviceType, PATH_ANNOTATION_CLASS_NAME);
+ }
+
+ @Override
+ protected boolean isRestCapableMethod(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+ return isAnnotationPresent(serviceMethod, HTTP_METHOD_ANNOTATION_CLASS_NAME);
+ }
+
+ @Override
+ protected String resolveRequestMethod(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+ Annotation httpMethod = findMetaAnnotation(serviceMethod, HTTP_METHOD_ANNOTATION_CLASS_NAME);
+ return getValue(httpMethod);
+ }
+
+ @Override
+ protected String resolveRequestPath(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+ String requestBasePath = resolveRequestPathFromType(serviceType, serviceInterfaceClass);
+ String requestRelativePath = resolveRequestPathFromMethod(serviceMethod);
+ return buildPath(requestBasePath, requestRelativePath);
+ }
+
+ private String resolveRequestPathFromType(Class<?> serviceType, Class<?> serviceInterfaceClass) {
+ Annotation path = findAnnotation(serviceType, PATH_ANNOTATION_CLASS_NAME);
+ if (path == null) {
+ path = findAnnotation(serviceInterfaceClass, PATH_ANNOTATION_CLASS_NAME);
+ }
+ return getValue(path);
+ }
+
+ private String resolveRequestPathFromMethod(Method serviceMethod) {
+ Annotation path = findAnnotation(serviceMethod, PATH_ANNOTATION_CLASS_NAME);
+ return getValue(path);
+ }
+
+ @Override
+ protected void processProduces(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass,
+ Set<String> produces) {
+ addAnnotationValues(serviceMethod, PRODUCES_ANNOTATION_CLASS_NAME, produces);
+ }
+
+ @Override
+ protected void processConsumes(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass,
+ Set<String> consumes) {
+ addAnnotationValues(serviceMethod, CONSUMES_ANNOTATION_CLASS_NAME, consumes);
+ }
+
+ private void addAnnotationValues(Method serviceMethod, String annotationAttributeName, Set<String> result) {
+ Annotation annotation = findAnnotation(serviceMethod, 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
new file mode 100644
index 0000000..894f33d
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/MatrixParamParameterProcessor.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.metadata.rest.jaxrs;
+
+import org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor;
+
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.MATRIX_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @MatrixParam
+ *
+ * @since 2.7.6
+ */
+public class MatrixParamParameterProcessor extends ParamAnnotationParameterProcessor {
+
+ @Override
+ public String getAnnotationType() {
+ 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
new file mode 100644
index 0000000..b6be293
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/ParamAnnotationParameterProcessor.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.metadata.rest.jaxrs;
+
+import org.apache.dubbo.metadata.rest.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.rest.RequestMetadata;
+import org.apache.dubbo.metadata.rest.RestMethodMetadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+/**
+ * The abstract {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @*Param
+ */
+public abstract class ParamAnnotationParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+ @Override
+ protected void process(String name, String defaultValue, Annotation annotation, Object 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/QueryParamParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/QueryParamParameterProcessor.java
new file mode 100644
index 0000000..bfe539c
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/jaxrs/QueryParamParameterProcessor.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.metadata.rest.jaxrs;
+
+import org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor;
+
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.QUERY_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @QueryParam
+ *
+ * @since 2.7.6
+ */
+public class QueryParamParameterProcessor extends ParamAnnotationParameterProcessor {
+
+ @Override
+ public String getAnnotationType() {
+ return QUERY_PARAM_ANNOTATION_CLASS_NAME;
+ }
+}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/AbstractRequestAnnotationParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/AbstractRequestAnnotationParameterProcessor.java
new file mode 100644
index 0000000..28407c2
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/AbstractRequestAnnotationParameterProcessor.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.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.common.utils.AnnotationUtils.getAttribute;
+
+/**
+ * The abstract {@link AnnotatedMethodParameterProcessor} implementation for Spring Web MVC's @Request*
+ */
+public abstract class AbstractRequestAnnotationParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+ @Override
+ protected String getAnnotationValue(Annotation annotation, Parameter parameter, int parameterIndex) {
+ // try to get "value" attribute first
+ String name = super.getAnnotationValue(annotation, parameter, parameterIndex);
+
+ // try to get "name" attribute if required
+ if (isEmpty(name)) {
+ name = getAttribute(annotation, "name");
+ }
+
+ // finally , try to the name of parameter
+ if (isEmpty(name)) {
+ name = parameter.getName();
+ }
+
+ return name;
+ }
+
+ @Override
+ protected String getDefaultValue(Annotation annotation, Parameter parameter, int parameterIndex) {
+ String defaultValue = getAttribute(annotation, "defaultValue");
+ if (isEmpty(defaultValue)) {
+ defaultValue = super.getDefaultValue(annotation, parameter, parameterIndex);
+ }
+ return defaultValue;
+ }
+
+ protected boolean isEmpty(String str) {
+ return str == null || str.isEmpty();
+ }
+}
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
new file mode 100644
index 0000000..8d16898
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/RequestHeaderParameterProcessor.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.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.rest.RestMethodMetadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_HEADER_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for Spring Web MVC's @RequestHeader
+ */
+public class RequestHeaderParameterProcessor extends AbstractRequestAnnotationParameterProcessor {
+
+ @Override
+ public String getAnnotationType() {
+ return REQUEST_HEADER_ANNOTATION_CLASS_NAME;
+ }
+
+ @Override
+ protected void process(String name, String defaultValue, Annotation annotation, Object 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
new file mode 100644
index 0000000..5ae4727
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/RequestParamParameterProcessor.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.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.rest.RestMethodMetadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for Spring Web MVC's @RequestParam
+ */
+public class RequestParamParameterProcessor extends AbstractRequestAnnotationParameterProcessor {
+
+ @Override
+ public String getAnnotationType() {
+ return REQUEST_PARAM_ANNOTATION_CLASS_NAME;
+ }
+
+ @Override
+ protected void process(String name, String defaultValue, Annotation annotation, Object 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
new file mode 100644
index 0000000..2fde671
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java
@@ -0,0 +1,128 @@
+/*
+ * 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.AbstractServiceRestMetadataResolver;
+import org.apache.dubbo.metadata.rest.ServiceRestMetadataResolver;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+import static java.lang.String.valueOf;
+import static java.lang.reflect.Array.getLength;
+import static java.util.stream.Stream.of;
+import static org.apache.dubbo.common.utils.AnnotationUtils.findAnnotation;
+import static org.apache.dubbo.common.utils.AnnotationUtils.findMetaAnnotation;
+import static org.apache.dubbo.common.utils.AnnotationUtils.getAttribute;
+import static org.apache.dubbo.common.utils.AnnotationUtils.isAnnotationPresent;
+import static org.apache.dubbo.common.utils.ArrayUtils.isEmpty;
+import static org.apache.dubbo.common.utils.ArrayUtils.isNotEmpty;
+import static org.apache.dubbo.common.utils.HttpUtils.buildPath;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.CONTROLLER_ANNOTATION_CLASS_NAME;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_MAPPING_ANNOTATION_CLASS_NAME;
+
+/**
+ * {@link ServiceRestMetadataResolver}
+ *
+ * @since 2.7.6
+ */
+public class SpringMvcServiceRestMetadataResolver extends AbstractServiceRestMetadataResolver {
+
+ private static final int FIRST_ELEMENT_INDEX = 0;
+
+ @Override
+ protected boolean supports0(Class<?> serviceType) {
+ return isAnnotationPresent(serviceType, CONTROLLER_ANNOTATION_CLASS_NAME);
+ }
+
+ @Override
+ protected boolean isRestCapableMethod(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+ return isAnnotationPresent(serviceType, REQUEST_MAPPING_ANNOTATION_CLASS_NAME);
+ }
+
+ @Override
+ protected String resolveRequestMethod(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+ String requestBasePath = resolveRequestPath(serviceType);
+ String requestRelativePath = resolveRequestPath(serviceMethod);
+ return buildPath(requestBasePath, requestRelativePath);
+ }
+
+ @Override
+ protected String resolveRequestPath(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+ Annotation requestMapping = getRequestMapping(serviceMethod);
+
+ // httpMethod is an array of RequestMethod
+ Object httpMethod = getAttribute(requestMapping, "method");
+
+ if (httpMethod == null || getLength(httpMethod) < 1) {
+ return null;
+ }
+
+ // TODO Is is required to support more request methods?
+ return valueOf(Array.get(httpMethod, FIRST_ELEMENT_INDEX));
+ }
+
+ @Override
+ protected void processProduces(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, Set<String> produces) {
+ addMediaTypes(serviceMethod, "produces", produces);
+ }
+
+ @Override
+ protected void processConsumes(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, Set<String> consumes) {
+ addMediaTypes(serviceMethod, "consumes", consumes);
+ }
+
+ private String resolveRequestPath(AnnotatedElement annotatedElement) {
+ Annotation mappingAnnotation = getRequestMapping(annotatedElement);
+ // try "value" first
+ String[] value = getAttribute(mappingAnnotation, "value");
+
+ if (isEmpty(value)) { // try "path" later
+ value = getAttribute(mappingAnnotation, "path");
+ }
+
+ if (isEmpty(value)) {
+ return "";
+ }
+ // TODO Is is required to support more request paths?
+ return value[FIRST_ELEMENT_INDEX];
+ }
+
+ private void addMediaTypes(Method serviceMethod, String annotationAttributeName, Set<String> mediaTypesSet) {
+
+ Annotation mappingAnnotation = getRequestMapping(serviceMethod);
+
+ 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_NAME);
+ // try the annotation meta-annotated later
+ if (requestMapping == null) {
+ requestMapping = findMetaAnnotation(annotatedElement, REQUEST_MAPPING_ANNOTATION_CLASS_NAME);
+ }
+ return requestMapping;
+ }
+}
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
new file mode 100644
index 0000000..bcb620c
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor
@@ -0,0 +1,10 @@
+# JAX-RS's implementations
+jax-rs.query-param = org.apache.dubbo.metadata.rest.jaxrs.QueryParamParameterProcessor
+jax-rs.form-param = org.apache.dubbo.metadata.rest.jaxrs.FormParamParameterProcessor
+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
+
+# 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
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.ServiceRestMetadataResolver b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.ServiceRestMetadataResolver
new file mode 100644
index 0000000..5b0873b
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.rest.ServiceRestMetadataResolver
@@ -0,0 +1,3 @@
+default =
+jax-rs = org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver
+spring-webmvc = org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver
\ No newline at end of file
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuildderTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuilderTest.java
similarity index 97%
rename from dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuildderTest.java
rename to dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuilderTest.java
index 89c81a5..3762f3a 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuildderTest.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/definition/ServiceDefinitionBuilderTest.java
@@ -31,7 +31,7 @@ import java.util.List;
/**
* 2018/11/6
*/
-public class ServiceDefinitionBuildderTest {
+public class ServiceDefinitionBuilderTest {
@Test
public void testBuilderComplextObject() {
@@ -77,7 +77,7 @@ public class ServiceDefinitionBuildderTest {
}
}
Assertions.assertEquals(topTypeDefinition.getProperties().get("v").getType(), "long");
- Assertions.assertEquals(topTypeDefinition.getProperties().get("maps").getType(), "java.util.Map<java.lang.String, java.lang.String>");
+ Assertions.assertEquals(topTypeDefinition.getProperties().get("maps").getType(), "java.util.Map<java.lang.String,java.lang.String>");
Assertions.assertEquals(topTypeDefinition.getProperties().get("innerObject").getType(), ComplexObject.InnerObject.class.getName());
Assertions.assertEquals(topTypeDefinition.getProperties().get("intList").getType(), "java.util.List<java.lang.Integer>");
Assertions.assertEquals(topTypeDefinition.getProperties().get("strArrays").getType(), "java.lang.String[]");
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
new file mode 100644
index 0000000..210952a
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/DefaultRestService.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 org.apache.dubbo.metadata.rest;
+
+import org.apache.dubbo.config.annotation.Service;
+
+import java.util.Map;
+
+/**
+ * The default implementation of {@link RestService}
+ *
+ * @since 2.7.6
+ */
+@Service(version = "1.0.0")
+public class DefaultRestService implements RestService {
+
+ @Override
+ public String param(String param) {
+ return null;
+ }
+
+ @Override
+ public String params(int a, String b) {
+ return null;
+ }
+
+ @Override
+ public String headers(String header, String header2, Integer param) {
+ return null;
+ }
+
+ @Override
+ public String pathVariables(String path1, String path2, String param) {
+ return null;
+ }
+
+ @Override
+ public String form(String form) {
+ return null;
+ }
+
+ @Override
+ public User requestBodyMap(Map<String, Object> data, String param) {
+ return null;
+ }
+
+ @Override
+ public Map<String, Object> requestBodyUser(User user) {
+ return null;
+ }
+
+ 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
new file mode 100644
index 0000000..65d67ab
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/RestService.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.metadata.rest;
+
+
+import java.util.Map;
+
+/**
+ * An interface for REST service
+ *
+ * @since 2.7.6
+ */
+public interface RestService {
+
+ String param(String param);
+
+ String params(int a, String b);
+
+ String headers(String header, String header2, Integer param);
+
+ String pathVariables(String path1, String path2, String param);
+
+ String form(String form);
+
+ User requestBodyMap(Map<String, Object> data, String param);
+
+ Map<String, Object> requestBodyUser(User user);
+}
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
new file mode 100644
index 0000000..ead5186
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.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.config.annotation.Service;
+
+import org.springframework.http.MediaType;
+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 java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Spring MVC {@link RestService}
+ *
+ * @since 2.7.6
+ */
+@Service(version = "2.0.0")
+@RestController
+public class SpringRestService implements RestService {
+
+ @Override
+ @GetMapping(value = "/param")
+ public String param(@RequestParam(defaultValue = "value-param") String param) {
+ return param;
+ }
+
+ @Override
+ @PostMapping("/params")
+ public String params(@RequestParam(defaultValue = "value-a") int a, @RequestParam(defaultValue = "value-b") String b) {
+ return a + b;
+ }
+
+ @Override
+ @GetMapping("/headers")
+ public String headers(@RequestHeader(name = "h", defaultValue = "value-h") String header,
+ @RequestHeader(name = "h2", defaultValue = "value-h2") String header2,
+ @RequestParam(value = "v", defaultValue = "1") Integer param) {
+ String result = header + " , " + header2 + " , " + param;
+ return result;
+ }
+
+ @Override
+ @GetMapping("/path-variables/{p1}/{p2}")
+ public String pathVariables(@PathVariable("p1") String path1,
+ @PathVariable("p2") String path2, @RequestParam("v") String param) {
+ String result = path1 + " , " + path2 + " , " + param;
+ return result;
+ }
+
+ @Override
+ @PostMapping("/form")
+ public String form(@RequestParam("f") String form) {
+ return String.valueOf(form);
+ }
+
+ @Override
+ @PostMapping(value = "/request/body/map", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public User requestBodyMap(@RequestBody Map<String, Object> data,
+ @RequestParam("param") String param) {
+ User user = new User();
+ user.setId(((Integer) data.get("id")).longValue());
+ user.setName((String) data.get("name"));
+ user.setAge((Integer) data.get("age"));
+ return user;
+ }
+
+ @PostMapping(value = "/request/body/user", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ @Override
+ public Map<String, Object> requestBodyUser(@RequestBody User user) {
+ Map<String, Object> map = new HashMap<>();
+ map.put("id", user.getId());
+ map.put("name", user.getName());
+ map.put("age", user.getAge());
+ return map;
+ }
+}
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
new file mode 100644
index 0000000..f5d94b7
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java
@@ -0,0 +1,107 @@
+/*
+ * 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.config.annotation.Service;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+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 java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * JAX-RS {@link RestService}
+ */
+@Service(version = "3.0.0", protocol = {"dubbo", "rest"}, group = "standard")
+@Path("/")
+public class StandardRestService implements RestService {
+
+ @Override
+ @Path("param")
+ @GET
+ public String param(@QueryParam("param") String param) {
+ return param;
+ }
+
+ @Override
+ @Path("params")
+ @POST
+ public String params(@QueryParam("a") int a, @QueryParam("b") String b) {
+ return a + b;
+ }
+
+ @Override
+ @Path("headers")
+ @GET
+ public String headers(@HeaderParam("h") String header,
+ @HeaderParam("h2") String header2, @QueryParam("v") Integer param) {
+ String result = header + " , " + header2 + " , " + param;
+ return result;
+ }
+
+ @Override
+ @Path("path-variables/{p1}/{p2}")
+ @GET
+ public String pathVariables(@PathParam("p1") String path1,
+ @PathParam("p2") String path2, @QueryParam("v") String param) {
+ String result = path1 + " , " + path2 + " , " + param;
+ return result;
+ }
+
+ // @CookieParam does not support : https://github.com/OpenFeign/feign/issues/913
+ // @CookieValue also does not support
+
+ @Override
+ @Path("form")
+ @POST
+ public String form(@FormParam("f") String form) {
+ return String.valueOf(form);
+ }
+
+ @Override
+ @Path("request/body/map")
+ @POST
+ @Produces("application/json;charset=UTF-8")
+ public User requestBodyMap(Map<String, Object> data,
+ @QueryParam("param") String param) {
+ User user = new User();
+ user.setId(((Integer) data.get("id")).longValue());
+ user.setName((String) data.get("name"));
+ user.setAge((Integer) data.get("age"));
+ return user;
+ }
+
+ @Path("request/body/user")
+ @POST
+ @Override
+ @Consumes("application/json;charset=UTF-8")
+ public Map<String, Object> requestBodyUser(User user) {
+ Map<String, Object> map = new HashMap<>();
+ map.put("id", user.getId());
+ map.put("name", user.getName());
+ map.put("age", user.getAge());
+ return map;
+ }
+}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/User.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/User.java
new file mode 100644
index 0000000..2ad6a51
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/User.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.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-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/resolver/jaxrs/JAXRSServiceRestMetadataResolverTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/resolver/jaxrs/JAXRSServiceRestMetadataResolverTest.java
new file mode 100644
index 0000000..d959007
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/resolver/jaxrs/JAXRSServiceRestMetadataResolverTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.resolver.jaxrs;
+
+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.jaxrs.JAXRSServiceRestMetadataResolver;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * {@link JAXRSServiceRestMetadataResolver} Test
+ *
+ * @since 2.7.6
+ */
+public class JAXRSServiceRestMetadataResolverTest {
+
+ private JAXRSServiceRestMetadataResolver instance = new JAXRSServiceRestMetadataResolver();
+
+ @Test
+ public void testSupports() {
+ // JAX-RS RestService class
+ assertTrue(instance.supports(StandardRestService.class));
+ // Spring MVC RestService class
+ assertFalse(instance.supports(SpringRestService.class));
+ // Default RestService class
+ assertFalse(instance.supports(DefaultRestService.class));
+ // No annotated RestService class
+ assertFalse(instance.supports(RestService.class));
+ // null
+ assertFalse(instance.supports(null));
+ }
+
+ @Test
+ public void testResolve() {
+ // Generated by "dubbo-metadata-processor"
+ ClassPathServiceRestMetadataReader reader = new ClassPathServiceRestMetadataReader("META-INF/dubbo/jax-rs-service-rest-metadata.json");
+ List<ServiceRestMetadata> serviceRestMetadataList = reader.read();
+
+ ServiceRestMetadata expectedServiceRestMetadata = serviceRestMetadataList.get(0);
+ ServiceRestMetadata serviceRestMetadata = instance.resolve(StandardRestService.class);
+
+
+ List<RestMethodMetadata> meta1 = new LinkedList<>(expectedServiceRestMetadata.getMeta());
+ List<RestMethodMetadata> meta2 = new LinkedList<>(serviceRestMetadata.getMeta());
+
+ for (int i = 0; i < meta1.size(); i++) {
+ RestMethodMetadata restMethodMetadata = meta1.get(i);
+ RestMethodMetadata restMethodMetadata2 = meta2.get(i);
+ assertEquals(restMethodMetadata, restMethodMetadata2);
+ }
+
+ assertEquals(expectedServiceRestMetadata, serviceRestMetadata);
+
+ }
+}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/resources/META-INF/dubbo/jax-rs-service-rest-metadata.json b/dubbo-metadata/dubbo-metadata-api/src/test/resources/META-INF/dubbo/jax-rs-service-rest-metadata.json
new file mode 100644
index 0000000..3830369
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/resources/META-INF/dubbo/jax-rs-service-rest-metadata.json
@@ -0,0 +1,349 @@
+[
+ {
+ "serviceInterface": "org.apache.dubbo.metadata.rest.RestService",
+ "version": "3.0.0",
+ "group": "standard",
+ "meta": [
+ {
+ "method": {
+ "name": "form",
+ "parameterTypes": [
+ "java.lang.String"
+ ],
+ "returnType": "java.lang.String",
+ "parameters": [
+ {
+ "type": "java.lang.String",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ }
+ ]
+ },
+ "request": {
+ "method": "POST",
+ "path": "/form",
+ "params": {
+ "f": [
+ "{0}"
+ ]
+ },
+ "headers": {},
+ "consumes": [],
+ "produces": []
+ },
+ "indexToName": {
+ "0": [
+ "form"
+ ]
+ }
+ },
+ {
+ "method": {
+ "name": "headers",
+ "parameterTypes": [
+ "java.lang.String",
+ "java.lang.String",
+ "java.lang.Integer"
+ ],
+ "returnType": "java.lang.String",
+ "parameters": [
+ {
+ "type": "java.lang.String",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ },
+ {
+ "type": "java.lang.String",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ },
+ {
+ "type": "java.lang.Integer",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ }
+ ]
+ },
+ "request": {
+ "method": "GET",
+ "path": "/headers",
+ "params": {
+ "v": [
+ "{2}"
+ ]
+ },
+ "headers": {
+ "h": [
+ "{0}"
+ ],
+ "h2": [
+ "{1}"
+ ]
+ },
+ "consumes": [],
+ "produces": []
+ },
+ "indexToName": {
+ "0": [
+ "header"
+ ],
+ "1": [
+ "header2"
+ ],
+ "2": [
+ "param"
+ ]
+ }
+ },
+ {
+ "method": {
+ "name": "param",
+ "parameterTypes": [
+ "java.lang.String"
+ ],
+ "returnType": "java.lang.String",
+ "parameters": [
+ {
+ "type": "java.lang.String",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ }
+ ]
+ },
+ "request": {
+ "method": "GET",
+ "path": "/param",
+ "params": {
+ "param": [
+ "{0}"
+ ]
+ },
+ "headers": {},
+ "consumes": [],
+ "produces": []
+ },
+ "indexToName": {
+ "0": [
+ "param"
+ ]
+ }
+ },
+ {
+ "method": {
+ "name": "params",
+ "parameterTypes": [
+ "int",
+ "java.lang.String"
+ ],
+ "returnType": "java.lang.String",
+ "parameters": [
+ {
+ "type": "int",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ },
+ {
+ "type": "java.lang.String",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ }
+ ]
+ },
+ "request": {
+ "method": "POST",
+ "path": "/params",
+ "params": {
+ "a": [
+ "{0}"
+ ],
+ "b": [
+ "{1}"
+ ]
+ },
+ "headers": {},
+ "consumes": [],
+ "produces": []
+ },
+ "indexToName": {
+ "0": [
+ "a"
+ ],
+ "1": [
+ "b"
+ ]
+ }
+ },
+ {
+ "method": {
+ "name": "pathVariables",
+ "parameterTypes": [
+ "java.lang.String",
+ "java.lang.String",
+ "java.lang.String"
+ ],
+ "returnType": "java.lang.String",
+ "parameters": [
+ {
+ "type": "java.lang.String",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ },
+ {
+ "type": "java.lang.String",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ },
+ {
+ "type": "java.lang.String",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ }
+ ]
+ },
+ "request": {
+ "method": "GET",
+ "path": "/path-variables/{p1}/{p2}",
+ "params": {
+ "v": [
+ "{2}"
+ ]
+ },
+ "headers": {},
+ "consumes": [],
+ "produces": []
+ },
+ "indexToName": {
+ "0": [
+ "path1"
+ ],
+ "1": [
+ "path2"
+ ],
+ "2": [
+ "param"
+ ]
+ }
+ },
+ {
+ "method": {
+ "name": "requestBodyMap",
+ "parameterTypes": [
+ "java.util.Map\u003cjava.lang.String,java.lang.Object\u003e",
+ "java.lang.String"
+ ],
+ "returnType": "org.apache.dubbo.metadata.rest.User",
+ "parameters": [
+ {
+ "type": "java.util.Map\u003cjava.lang.String,java.lang.Object\u003e",
+ "items": [
+ {
+ "type": "java.lang.String",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ },
+ {
+ "type": "java.lang.Object",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ }
+ ],
+ "enum": [],
+ "properties": {}
+ },
+ {
+ "type": "java.lang.String",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ }
+ ]
+ },
+ "request": {
+ "method": "POST",
+ "path": "/request/body/map",
+ "params": {
+ "param": [
+ "{1}"
+ ]
+ },
+ "headers": {},
+ "consumes": [],
+ "produces": [
+ "application/json;charset\u003dUTF-8"
+ ]
+ },
+ "indexToName": {
+ "0": [
+ "data"
+ ],
+ "1": [
+ "param"
+ ]
+ }
+ },
+ {
+ "method": {
+ "name": "requestBodyUser",
+ "parameterTypes": [
+ "org.apache.dubbo.metadata.rest.User"
+ ],
+ "returnType": "java.util.Map\u003cjava.lang.String,java.lang.Object\u003e",
+ "parameters": [
+ {
+ "type": "org.apache.dubbo.metadata.rest.User",
+ "items": [],
+ "enum": [],
+ "properties": {
+ "name": {
+ "type": "java.lang.String",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ },
+ "id": {
+ "type": "java.lang.Long",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ },
+ "age": {
+ "type": "java.lang.Integer",
+ "items": [],
+ "enum": [],
+ "properties": {}
+ }
+ }
+ }
+ ]
+ },
+ "request": {
+ "method": "POST",
+ "path": "/request/body/user",
+ "params": {},
+ "headers": {},
+ "consumes": [
+ "application/json;charset\u003dUTF-8"
+ ],
+ "produces": []
+ },
+ "indexToName": {
+ "0": [
+ "user"
+ ]
+ }
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/dubbo-metadata/dubbo-metadata-definition-protobuf/src/test/java/org/apache/dubbo/metadata/definition/protobuf/ProtobufTypeBuilderTest.java b/dubbo-metadata/dubbo-metadata-definition-protobuf/src/test/java/org/apache/dubbo/metadata/definition/protobuf/ProtobufTypeBuilderTest.java
index 5557e26..e9c8a17 100644
--- a/dubbo-metadata/dubbo-metadata-definition-protobuf/src/test/java/org/apache/dubbo/metadata/definition/protobuf/ProtobufTypeBuilderTest.java
+++ b/dubbo-metadata/dubbo-metadata-definition-protobuf/src/test/java/org/apache/dubbo/metadata/definition/protobuf/ProtobufTypeBuilderTest.java
@@ -66,8 +66,8 @@ public class ProtobufTypeBuilderTest {
assertThat(propertiesMap.containsKey("phone"), is(true));
assertThat(propertiesMap.get("phone").getType(), equalTo("java.util.List<org.apache.dubbo.metadata.definition.protobuf.model.GooglePB$PhoneNumber>"));
assertThat(propertiesMap.containsKey("doubleMap"), is(true));
- assertThat(propertiesMap.get("doubleMap").getType(), equalTo("java.util.Map<java.lang.String, org.apache.dubbo.metadata.definition.protobuf.model.GooglePB$PhoneNumber>"));
+ assertThat(propertiesMap.get("doubleMap").getType(), equalTo("java.util.Map<java.lang.String,org.apache.dubbo.metadata.definition.protobuf.model.GooglePB$PhoneNumber>"));
assertThat(propertiesMap.get("bytesList").getType(), equalTo("java.util.List<com.google.protobuf.ByteString>"));
- assertThat(propertiesMap.get("bytesMap").getType(), equalTo("java.util.Map<java.lang.String, com.google.protobuf.ByteString>"));
+ assertThat(propertiesMap.get("bytesMap").getType(), equalTo("java.util.Map<java.lang.String,com.google.protobuf.ByteString>"));
}
}
diff --git a/dubbo-metadata/dubbo-metadata-processor/pom.xml b/dubbo-metadata/dubbo-metadata-processor/pom.xml
new file mode 100644
index 0000000..965c036
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-processor/pom.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-metadata</artifactId>
+ <version>${revision}</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>dubbo-metadata-processor</artifactId>
+ <packaging>jar</packaging>
+ <name>dubbo-metadata-processor</name>
+ <description>The metadata processor module of Dubbo project</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-metadata-api</artifactId>
+ <version>${project.parent.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-rpc-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-cluster</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-common</artifactId>
+ <version>${project.parent.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>javax.annotation</groupId>
+ <artifactId>javax.annotation-api</artifactId>
+ </exclusion>
+
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+
+ <exclusion>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </exclusion>
+
+ <exclusion>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </exclusion>
+
+ <exclusion>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.javassist</groupId>
+ <artifactId>javassist</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.alibaba</groupId>
+ <artifactId>hessian-lite</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.alibaba</groupId>
+ <artifactId>fastjson</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.esotericsoftware</groupId>
+ <artifactId>kryo</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>de.javakaffee</groupId>
+ <artifactId>kryo-serializers</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>de.ruedigermoeller</groupId>
+ <artifactId>fst</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </exclusion>
+ </exclusions>
+
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ </dependency>
+
+ <!-- Test -->
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-config-api</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-registry-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-metadata-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-monitor-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-remoting-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-rpc-injvm</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-filter-validation</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-filter-cache</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <!-- JAX-RS API -->
+ <dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>javax.ws.rs-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- Spring Web MVC -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/AbstractServiceAnnotationProcessor.java b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/AbstractServiceAnnotationProcessor.java
new file mode 100644
index 0000000..7ef7794
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/AbstractServiceAnnotationProcessor.java
@@ -0,0 +1,108 @@
+/*
+ * 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.annotation.processing;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.Processor;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static javax.lang.model.util.ElementFilter.methodsIn;
+import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.SUPPORTED_ANNOTATION_TYPES;
+
+/**
+ * Abstract {@link Processor} for the classes that were annotated by Dubbo's @Service
+ *
+ * @since 2.7.6
+ */
+public abstract class AbstractServiceAnnotationProcessor extends AbstractProcessor {
+
+ protected Elements elements;
+
+ private List<? extends Element> objectMembers;
+
+ public synchronized void init(ProcessingEnvironment processingEnv) {
+ super.init(processingEnv);
+ this.elements = processingEnv.getElementUtils();
+ this.objectMembers = elements.getAllMembers(elements.getTypeElement(Object.class.getName()));
+ }
+
+ protected List<? extends Element> getActualMembers(TypeElement type) {
+ List<? extends Element> members = new LinkedList<>(elements.getAllMembers(type));
+ members.removeAll(objectMembers);
+ return members;
+ }
+
+ protected List<? extends ExecutableElement> getActualMethods(TypeElement type) {
+ return methodsIn(getActualMembers(type));
+ }
+
+ protected Map<String, ExecutableElement> getActualMethodsMap(TypeElement type) {
+ Map<String, ExecutableElement> methodsMap = new HashMap<>();
+ getActualMethods(type).forEach(method -> {
+ methodsMap.put(method.toString(), method);
+ });
+ return methodsMap;
+ }
+
+ public static String getMethodSignature(ExecutableElement method) {
+ if (!ElementKind.METHOD.equals(method.getKind())) {
+ throw new IllegalArgumentException("The argument must be Method Kind");
+ }
+
+ StringBuilder methodSignatureBuilder = new StringBuilder();
+
+ method.getModifiers().forEach(member -> {
+ methodSignatureBuilder.append(member).append(" ");
+ });
+
+ methodSignatureBuilder.append(method.getReturnType())
+ .append(" ")
+ .append(method.toString());
+
+ return methodSignatureBuilder.toString();
+ }
+
+ protected TypeElement getTypeElement(CharSequence className) {
+ return elements.getTypeElement(className);
+ }
+
+ protected PackageElement getPackageElement(Element type) {
+ return this.elements.getPackageOf(type);
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latest();
+ }
+
+ @Override
+ public final Set<String> getSupportedAnnotationTypes() {
+ return SUPPORTED_ANNOTATION_TYPES;
+ }
+}
diff --git a/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/ClassPathMetadataStorage.java b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/ClassPathMetadataStorage.java
new file mode 100644
index 0000000..9e1032c
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/ClassPathMetadataStorage.java
@@ -0,0 +1,105 @@
+/*
+ * 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.annotation.processing;
+
+
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.tools.FileObject;
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import static java.util.Optional.empty;
+import static java.util.Optional.ofNullable;
+import static javax.tools.StandardLocation.CLASS_OUTPUT;
+import static org.apache.dubbo.metadata.annotation.processing.util.LoggerUtils.info;
+import static org.apache.dubbo.metadata.annotation.processing.util.LoggerUtils.warn;
+
+/**
+ * A storage class for metadata under class path
+ */
+public class ClassPathMetadataStorage {
+
+ private final Filer filer;
+
+ public ClassPathMetadataStorage(ProcessingEnvironment processingEnv) {
+ this.filer = processingEnv.getFiler();
+ }
+
+ public void write(Supplier<String> contentSupplier, String resourceName) {
+ try (Writer writer = getWriter(resourceName)) {
+ writer.write(contentSupplier.get());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public <T> Optional<T> read(String resourceName, Function<Reader, T> consumer) {
+ if (exists(resourceName)) {
+ try (Reader reader = getReader(resourceName)) {
+ return ofNullable(consumer.apply(reader));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return empty();
+ }
+
+ private boolean exists(String resourceName) {
+
+ return getResource(resourceName)
+ .map(FileObject::toUri)
+ .map(File::new)
+ .map(File::exists)
+ .orElse(false);
+ }
+
+ private Reader getReader(String resourceName) {
+ return getResource(resourceName).map(fileObject -> {
+ try {
+ return fileObject.openReader(false);
+ } catch (IOException e) {
+ }
+ return null;
+ }).orElse(null);
+ }
+
... 7990 lines suppressed ...