From 8bc9257f4e09b2937e21842b90984dfcaefe97b6 Mon Sep 17 00:00:00 2001 From: Albumen Kevin Date: Tue, 1 Jun 2021 14:14:35 +0800 Subject: [PATCH] Migrate master bugfix (#7927) --- CONTRIBUTING.md | 42 +- NOTICE | 2 +- PULL_REQUEST_TEMPLATE.md | 8 +- README.md | 66 +-- SECURITY.md | 6 +- dubbo-cluster/pom.xml | 6 + .../org/apache/dubbo/rpc/cluster/Cluster.java | 6 +- .../apache/dubbo/rpc/cluster/Constants.java | 6 + .../apache/dubbo/rpc/cluster/RouterChain.java | 24 +- .../configurator/AbstractConfigurator.java | 21 +- .../configurator/parser/ConfigParser.java | 21 +- .../governance/GovernanceRuleRepository.java | 2 +- .../ConsistentHashLoadBalance.java | 22 +- .../loadbalance/RandomLoadBalance.java | 18 +- .../router/condition/ConditionRouter.java | 65 +- .../condition/ConditionRouterFactory.java | 2 +- .../rpc/cluster/router/tag/TagRouter.java | 17 +- .../support/BroadcastClusterInvoker.java | 61 +- .../rpc/cluster/support/ClusterUtils.java | 32 +- .../support/FailoverClusterInvoker.java | 20 +- .../support/MergeableClusterInvoker.java | 2 +- .../registry/ZoneAwareClusterInvoker.java | 5 +- .../support/wrapper/AbstractCluster.java | 2 +- .../support/wrapper/MockClusterInvoker.java | 4 +- .../RoundRobinLoadBalanceTest.java | 8 +- .../router/condition/ConditionRouterTest.java | 34 ++ .../support/FailoverClusterInvokerTest.java | 28 + .../dubbo/common/BaseServiceMetadata.java | 10 +- .../org/apache/dubbo/common/Parameters.java | 3 + .../java/org/apache/dubbo/common/URL.java | 4 +- .../org/apache/dubbo/common/URLStrParser.java | 2 +- .../beanutil/JavaBeanSerializeUtil.java | 2 +- .../dubbo/common/bytecode/ClassGenerator.java | 4 +- .../bytecode/CustomizedLoaderClassPath.java | 3 +- .../apache/dubbo/common/bytecode/Mixin.java | 2 +- .../apache/dubbo/common/bytecode/Proxy.java | 3 - .../apache/dubbo/common/bytecode/Wrapper.java | 45 +- .../common/compiler/support/ClassUtils.java | 14 +- .../compiler/support/CtClassBuilder.java | 24 +- .../common/compiler/support/JdkCompiler.java | 34 +- .../AbstractDynamicConfiguration.java | 2 +- .../configcenter/ConfigChangedEvent.java | 8 +- .../common/config/configcenter/Constants.java | 1 + .../TreePathDynamicConfiguration.java | 4 +- .../dubbo/common/constants/ClusterRules.java | 63 ++ .../common/constants/CommonConstants.java | 16 + .../common/constants/LoadbalanceRules.java | 51 ++ .../common/extension/ExtensionLoader.java | 47 +- .../dubbo/common/extension/Wrapper.java | 4 + .../apache/dubbo/common/json/J2oVisitor.java | 8 +- .../apache/dubbo/common/lang/Prioritized.java | 2 +- .../common/threadlocal/InternalRunnable.java | 53 ++ .../threadlocal/InternalThreadLocal.java | 2 +- .../NamedInternalThreadFactory.java | 2 +- .../common/threadpool/ThreadlessExecutor.java | 35 +- .../support/AbortPolicyWithReport.java | 37 +- .../dubbo/common/timer/HashedWheelTimer.java | 6 +- .../dubbo/common/url/component/URLParam.java | 34 +- .../dubbo/common/utils/AnnotationUtils.java | 30 + .../apache/dubbo/common/utils/ArrayUtils.java | 12 + .../apache/dubbo/common/utils/ClassUtils.java | 30 +- .../dubbo/common/utils/CollectionUtils.java | 43 +- .../common/utils/CompatibleTypeUtils.java | 33 +- .../dubbo/common/utils/ExecutorUtil.java | 8 +- .../apache/dubbo/common/utils/FieldUtils.java | 5 + .../apache/dubbo/common/utils/IOUtils.java | 39 ++ .../apache/dubbo/common/utils/LFUCache.java | 21 +- .../dubbo/common/utils/MethodUtils.java | 18 +- .../apache/dubbo/common/utils/NetUtils.java | 10 +- .../apache/dubbo/common/utils/PojoUtils.java | 15 +- .../dubbo/common/utils/ReflectUtils.java | 49 +- .../utils/ServiceAnnotationResolver.java | 25 +- .../apache/dubbo/common/utils/UrlUtils.java | 18 +- .../apache/dubbo/config/AbstractConfig.java | 17 +- .../dubbo/config/AbstractInterfaceConfig.java | 77 ++- .../dubbo/config/AbstractReferenceConfig.java | 22 - .../dubbo/config/ApplicationConfig.java | 13 - .../org/apache/dubbo/config/Constants.java | 2 + .../apache/dubbo/config/ConsumerConfig.java | 19 +- .../dubbo/config/MetadataReportConfig.java | 14 + .../org/apache/dubbo/config/MethodConfig.java | 27 +- .../org/apache/dubbo/config/ModuleConfig.java | 13 - .../apache/dubbo/config/MonitorConfig.java | 13 - .../apache/dubbo/config/ProtocolConfig.java | 68 ++- .../apache/dubbo/config/ProviderConfig.java | 55 +- .../dubbo/config/ReferenceConfigBase.java | 16 +- .../apache/dubbo/config/RegistryConfig.java | 13 - .../dubbo/config/ServiceConfigBase.java | 56 +- .../org/apache/dubbo/config/SslConfig.java | 28 +- .../config/annotation/DubboReference.java | 10 +- .../dubbo/config/annotation/DubboService.java | 9 +- .../context/ConfigConfigurationAdapter.java | 2 +- .../dubbo/config/context/ConfigManager.java | 17 +- .../dubbo/event/DirectEventDispatcher.java | 1 + .../apache/dubbo/event/EventDispatcher.java | 2 +- .../dubbo/event/ParallelEventDispatcher.java | 1 + .../dubbo/rpc/support/ProtocolUtils.java | 7 + .../dubbo/common/BaseServiceMetadataTest.java | 66 +++ .../PojoUtilsForNonPublicStaticTest.java | 50 ++ .../apache/dubbo/common/URLStrParserTest.java | 56 +- .../java/org/apache/dubbo/common/URLTest.java | 112 ++++ .../dubbo/common/bytecode/WrapperTest.java | 52 +- .../compiler/support/JdkCompilerTest.java | 31 + .../configcenter/ConfigChangedEventTest.java | 30 +- .../common/extension/ExtensionLoaderTest.java | 91 ++- .../duplicated/DuplicatedOverriddenExt.java | 28 + .../DuplicatedWithoutOverriddenExt.java | 29 + .../impl/DuplicatedOverriddenExt1.java | 27 + .../impl/DuplicatedOverriddenExt2.java | 27 + .../impl/DuplicatedWithoutOverriddenExt1.java | 27 + .../impl/DuplicatedWithoutOverriddenExt2.java | 27 + .../dubbo/common/extension/wrapper/Demo.java | 24 + .../common/extension/wrapper/WrapperTest.java | 41 ++ .../extension/wrapper/impl/DemoImpl.java | 26 + .../extension/wrapper/impl/DemoWrapper.java | 33 ++ .../extension/wrapper/impl/DemoWrapper2.java | 33 ++ .../common/model/SerializablePerson.java | 5 +- .../threadlocal/InternalThreadLocalTest.java | 1 + .../threadpool/ThreadlessExecutorTest.java | 58 ++ .../manager/ExecutorRepositoryTest.java | 79 +++ .../support/AbortPolicyWithReportTest.java | 52 ++ .../support/cached/CachedThreadPoolTest.java | 13 +- .../eager/EagerThreadPoolExecutorTest.java | 17 +- .../support/eager/EagerThreadPoolTest.java | 13 +- .../common/timer/HashedWheelTimerTest.java | 190 +++++- .../common/utils/CollectionUtilsTest.java | 2 +- .../common/utils/CompatibleTypeUtilsTest.java | 2 +- .../dubbo/common/utils/ConfigUtilsTest.java | 2 +- .../dubbo/common/utils/DefaultPageTest.java | 9 +- .../dubbo/common/utils/LFUCacheTest.java | 21 +- .../apache/dubbo/common/utils/LogTest.java | 55 +- .../dubbo/common/utils/MemberUtilsTest.java | 12 + .../dubbo/common/utils/MethodUtilsTest.java | 54 ++ .../dubbo/common/utils/NetUtilsTest.java | 11 +- .../dubbo/common/utils/PojoUtilsTest.java | 96 ++- .../dubbo/common/utils/ReflectUtilsTest.java | 34 +- ...tension.duplicated.DuplicatedOverriddenExt | 1 + ....duplicated.DuplicatedWithoutOverriddenExt | 1 + ...tension.duplicated.DuplicatedOverriddenExt | 1 + ....duplicated.DuplicatedWithoutOverriddenExt | 1 + ...apache.dubbo.common.extension.wrapper.Demo | 4 + .../registry/support/FailbackRegistry.java | 4 - .../rpc/protocol/dubbo/FutureAdapter.java | 25 +- .../apache/dubbo/echo/EchoServiceTest.java | 3 +- .../dubbo/generic/GenericServiceTest.java | 24 + dubbo-config/dubbo-config-api/pom.xml | 6 + .../apache/dubbo/config/ReferenceConfig.java | 47 +- .../apache/dubbo/config/ServiceConfig.java | 9 +- .../config/bootstrap/DubboBootstrap.java | 67 +-- .../DubboBootstrapStartStopListener.java | 34 ++ .../bootstrap/builders/ReferenceBuilder.java | 6 +- .../bootstrap/builders/ServiceBuilder.java | 8 +- .../config/utils/ConfigValidationUtils.java | 38 +- .../org.apache.dubbo.event.EventListener | 2 +- ...che.dubbo.metadata.MetadataServiceExporter | 1 - .../dubbo/config/AbstractConfigTest.java | 15 +- .../config/MetadataReportConfigTest.java | 40 ++ .../apache/dubbo/config/MethodConfigTest.java | 12 +- .../dubbo/config/ReferenceConfigTest.java | 38 +- .../dubbo/config/RegistryConfigTest.java | 28 + .../dubbo/config/ServiceConfigTest.java | 20 + ...blishingServiceDefinitionListenerTest.java | 1 + ...DefaultPropertyValueBeanPostProcessor.java | 11 +- .../DubboBootstrapApplicationListener.java | 17 +- ...otstrapStartStopListenerSpringAdapter.java | 48 ++ ...LifecycleComponentApplicationListener.java | 11 +- ...cutionApplicationContextEventListener.java | 71 --- .../context/annotation/EnableDubboConfig.java | 5 +- .../event/DubboBootstrapStatedEvent.java | 50 ++ .../event/DubboBootstrapStopedEvent.java | 50 ++ .../properties/DefaultDubboConfigBinder.java | 22 + .../extension/SpringExtensionFactory.java | 3 + .../spring/schema/DubboNamespaceHandler.java | 2 +- .../main/resources/META-INF/compat/dubbo.xsd | 10 + .../src/main/resources/META-INF/dubbo.xsd | 15 + ....bootstrap.DubboBootstrapStartStopListener | 1 + .../spring/registry/MockRegistryFactory.java | 8 +- .../spring/dubbo-generic-consumer.xml | 2 +- .../spring/dubbo-nacos-consumer-context.xml | 12 +- .../spring/dubbo-nacos-provider-context.xml | 6 +- .../apollo/ApolloDynamicConfiguration.java | 2 +- .../ApolloDynamicConfigurationTest.java | 17 +- .../support/apollo/EmbeddedApolloJunit5.java | 193 ++++++ .../support/zookeeper/CacheListener.java | 7 +- .../ZookeeperDynamicConfiguration.java | 15 +- .../resources/META-INF/assembly/bin/start.sh | 10 +- .../dubbo-demo-xml-consumer/pom.xml | 4 + .../dubbo-demo-xml-provider/pom.xml | 2 +- dubbo-dependencies-bom/pom.xml | 6 +- .../dubbo/cache/filter/CacheFilter.java | 2 +- .../cache/support/AbstractCacheFactory.java | 27 +- .../cache/support/expiring/ExpiringMap.java | 20 +- .../org.apache.dubbo.cache.CacheFactory | 3 +- .../expiring/ExpiringCacheFactoryTest.java | 76 ++- dubbo-metadata/dubbo-metadata-api/pom.xml | 7 + ...ynamicConfigurationServiceNameMapping.java | 2 +- .../definition/builder/MapTypeBuilder.java | 11 +- .../definition/model/MethodDefinition.java | 7 +- .../definition/model/TypeDefinition.java | 51 +- .../report/MetadataReportInstance.java | 3 + .../AbstractMetadataReportFactory.java | 8 +- .../AbstractServiceRestMetadataResolver.java | 6 +- .../metadata/rest/RestMetadataConstants.java | 33 ++ .../metadata/rest/RestMethodMetadata.java | 8 +- .../metadata/rest/ServiceRestMetadata.java | 8 +- ...ctRequestAnnotationParameterProcessor.java | 17 +- .../SpringMvcServiceRestMetadataResolver.java | 43 +- .../metadata/rest/SpringRestService.java | 4 +- .../metadata/rest/StandardRestService.java | 4 +- .../JAXRSServiceRestMetadataResolverTest.java | 3 +- ...ingMvcServiceRestMetadataResolverTest.java | 81 +++ .../spring-mvc-servoce-rest-metadata.json | 321 ++++++++++ .../protobuf/ProtobufTypeBuilderTest.java | 3 +- .../util/ServiceAnnotationUtils.java | 42 +- .../util/ServiceAnnotationUtilsTest.java | 8 +- .../metadata/rest/DefaultRestService.java | 4 +- .../metadata/rest/SpringRestService.java | 4 +- .../metadata/rest/StandardRestService.java | 4 +- .../ZookeeperMetadataReportTest.java | 556 +++++++++--------- .../dubbo/monitor/support/MonitorFilter.java | 6 +- .../dubbo/qos/legacy/ListTelnetHandler.java | 1 + .../dubbo/qos/legacy/LogTelnetHandler.java | 2 +- .../qos/server/handler/QosProcessHandler.java | 4 +- .../handler/TelnetIdleEventHandler.java | 42 ++ .../client/DefaultServiceInstance.java | 12 +- .../registry/client/InstanceAddressURL.java | 4 + .../registry/client/ServiceDiscovery.java | 12 +- .../ServiceDiscoveryRegistryDirectory.java | 3 +- .../ServiceInstancesChangedListener.java | 8 +- .../store/RemoteMetadataServiceImpl.java | 1 - .../client/migration/MigrationInvoker.java | 15 +- .../migration/MigrationRuleHandler.java | 2 - .../integration/DynamicDirectory.java | 7 +- .../integration/RegistryDirectory.java | 16 +- .../integration/RegistryProtocol.java | 2 +- .../registry/retry/FailedNotifiedTask.java | 67 --- .../registry/support/AbstractRegistry.java | 5 +- .../support/AbstractRegistryFactory.java | 20 +- .../registry/support/FailbackRegistry.java | 46 +- .../support/FailbackRegistryTest.java | 31 - .../registry/multicast/MulticastRegistry.java | 4 +- .../dubbo/registry/nacos/NacosRegistry.java | 20 +- .../registry/nacos/NacosRegistryFactory.java | 8 + .../registry/zookeeper/ZookeeperRegistry.java | 10 +- .../zookeeper/ZookeeperServiceDiscovery.java | 10 + ...ookeeperServiceDiscoveryChangeWatcher.java | 2 +- .../dubbo/remoting/exchange/Request.java | 10 + .../exchange/codec/ExchangeCodec.java | 141 +++-- .../exchange/support/DefaultFuture.java | 2 +- .../remoting/telnet/support/TelnetUtils.java | 4 +- .../support/command/LogTelnetHandler.java | 2 +- .../support/command/StatusTelnetHandler.java | 4 +- .../remoting/transport/AbstractClient.java | 78 ++- .../remoting/transport/AbstractCodec.java | 39 +- .../remoting/transport/AbstractEndpoint.java | 22 +- .../remoting/transport/AbstractServer.java | 23 +- .../remoting/transport/CodecSupport.java | 114 +++- .../transport/MultiMessageHandler.java | 13 +- .../dubbo/remoting/MockTransporter.java | 36 ++ .../dubbo/remoting/TransportersTest.java | 46 ++ .../remoting/codec/ExchangeCodecTest.java | 32 +- .../remoting/exchange/ExchangersTest.java | 58 ++ .../remoting/exchange/MockExchanger.java | 37 ++ .../exchange/support/DefaultFutureTest.java | 67 +++ .../remoting/telnet/TelnetUtilsTest.java | 72 +++ .../support/ClearTelnetHandlerTest.java | 46 ++ .../telnet/support/ExitTelnetHandlerTest.java | 38 ++ .../telnet/support/HelpTelnetHandlerTest.java | 48 ++ .../support/StatusTelnetHandlerTest.java | 44 ++ .../remoting/utils/PayloadDropperTest.java | 43 ++ .../dubbo/remoting/utils/UrlUtilsTest.java | 40 ++ .../org.apache.dubbo.remoting.Transporter | 1 + ...g.apache.dubbo.remoting.exchange.Exchanger | 1 + .../transport/netty4/NettyClient.java | 7 +- .../transport/netty4/NettyServer.java | 5 + .../curator/CuratorZookeeperClient.java | 12 +- dubbo-rpc/dubbo-rpc-api/pom.xml | 6 + .../org/apache/dubbo/rpc/AppResponse.java | 31 +- .../org/apache/dubbo/rpc/AsyncRpcResult.java | 6 +- .../apache/dubbo/rpc/AttachmentsAdapter.java | 6 +- .../java/org/apache/dubbo/rpc/Constants.java | 4 + .../org/apache/dubbo/rpc/RpcInvocation.java | 9 +- .../java/org/apache/dubbo/rpc/RpcStatus.java | 17 +- .../dubbo/rpc/filter/AccessLogFilter.java | 3 +- .../dubbo/rpc/filter/ExceptionFilter.java | 4 +- .../dubbo/rpc/filter/GenericFilter.java | 42 +- .../dubbo/rpc/filter/GenericImplFilter.java | 2 +- .../apache/dubbo/rpc/filter/TokenFilter.java | 4 +- .../apache/dubbo/rpc/filter/tps/StatItem.java | 2 +- .../dubbo/rpc/protocol/AbstractExporter.java | 9 +- .../dubbo/rpc/protocol/AbstractInvoker.java | 11 + .../rpc/protocol/AbstractProxyProtocol.java | 10 +- .../dubbo/rpc/proxy/AbstractProxyFactory.java | 5 +- .../dubbo/rpc/proxy/AbstractProxyInvoker.java | 4 +- .../rpc/proxy/InvokerInvocationHandler.java | 12 + .../dubbo/rpc/support/AccessLogData.java | 15 + .../apache/dubbo/rpc/support/RpcUtils.java | 2 +- .../org/apache/dubbo/rpc/RpcContextTest.java | 1 + .../dubbo/rpc/TimeoutCountDownTest.java | 39 ++ .../dubbo/rpc/filter/AccessLogFilterTest.java | 2 + .../rpc/filter/tps/DefaultTPSLimiterTest.java | 24 +- .../dubbo/rpc/filter/tps/StatItemTest.java | 11 + .../dubbo/rpc/support/RpcUtilsTest.java | 120 +++- .../dubbo/DecodeableRpcInvocation.java | 14 +- .../protocol/dubbo/DecodeableRpcResult.java | 14 +- .../dubbo/rpc/protocol/dubbo/DubboCodec.java | 40 +- .../rpc/protocol/dubbo/DubboCodecSupport.java | 53 ++ .../rpc/protocol/dubbo/DubboExporter.java | 3 +- .../rpc/protocol/dubbo/DubboInvoker.java | 3 +- .../rpc/protocol/dubbo/DubboProtocol.java | 106 ++-- .../dubbo/ReferenceCountExchangeClient.java | 21 +- ...st.java => DubboInvokerAvailableTest.java} | 3 +- .../rpc/protocol/dubbo/DubboProtocolTest.java | 18 + .../dubbo/decode/DubboTelnetDecodeTest.java | 4 +- .../protocol/dubbo/support/DemoService.java | 2 + .../dubbo/support/DemoServiceImpl.java | 8 + .../protocol/grpc/DubboHandlerRegistry.java | 9 +- .../rpc/protocol/injvm/InjvmExporter.java | 3 +- .../rpc/protocol/injvm/InjvmInvoker.java | 41 +- .../rpc/protocol/injvm/InjvmProtocol.java | 11 +- .../dubbo/rpc/protocol/injvm/DemoService.java | 2 + .../rpc/protocol/injvm/DemoServiceImpl.java | 11 + .../rpc/protocol/injvm/InjvmProtocolTest.java | 18 + .../rpc/protocol/tri/TripleProtocol.java | 3 +- .../hessian2/Hessian2ObjectInput.java | 10 +- .../hessian2/Hessian2ObjectOutput.java | 10 +- pom.xml | 40 +- 327 files changed, 6310 insertions(+), 1834 deletions(-) create mode 100644 dubbo-common/src/main/java/org/apache/dubbo/common/constants/ClusterRules.java create mode 100644 dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoadbalanceRules.java create mode 100644 dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalRunnable.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/BaseServiceMetadataTest.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/PojoUtilsForNonPublicStaticTest.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/DuplicatedOverriddenExt.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/DuplicatedWithoutOverriddenExt.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedOverriddenExt1.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedOverriddenExt2.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedWithoutOverriddenExt1.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedWithoutOverriddenExt2.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/Demo.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/WrapperTest.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoImpl.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper2.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/ThreadlessExecutorTest.java create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepositoryTest.java create mode 100644 dubbo-common/src/test/resources/META-INF/dubbo/external/org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt create mode 100644 dubbo-common/src/test/resources/META-INF/dubbo/external/org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt create mode 100644 dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt create mode 100644 dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt create mode 100644 dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.wrapper.Demo create mode 100644 dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrapStartStopListener.java create mode 100644 dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MetadataReportConfigTest.java create mode 100644 dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapStartStopListenerSpringAdapter.java delete mode 100644 dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/OneTimeExecutionApplicationContextEventListener.java create mode 100644 dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStatedEvent.java create mode 100644 dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStopedEvent.java create mode 100644 dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener create mode 100644 dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/EmbeddedApolloJunit5.java rename dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/{resolver => }/jaxrs/JAXRSServiceRestMetadataResolverTest.java (96%) create mode 100644 dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java create mode 100644 dubbo-metadata/dubbo-metadata-api/src/test/resources/META-INF/dubbo/spring-mvc-servoce-rest-metadata.json create mode 100644 dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/TelnetIdleEventHandler.java delete mode 100644 dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/retry/FailedNotifiedTask.java create mode 100644 dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/MockTransporter.java create mode 100644 dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/TransportersTest.java create mode 100644 dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/ExchangersTest.java create mode 100644 dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/MockExchanger.java create mode 100644 dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/TelnetUtilsTest.java create mode 100644 dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ClearTelnetHandlerTest.java create mode 100644 dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ExitTelnetHandlerTest.java create mode 100644 dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/HelpTelnetHandlerTest.java create mode 100644 dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/StatusTelnetHandlerTest.java create mode 100644 dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/PayloadDropperTest.java create mode 100644 dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/UrlUtilsTest.java create mode 100644 dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter create mode 100644 dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger create mode 100644 dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/TimeoutCountDownTest.java create mode 100644 dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodecSupport.java rename dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/{DubboInvokerAvilableTest.java => DubboInvokerAvailableTest.java} (99%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c92e4a115b..a1f163a36b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,18 +1,18 @@ -## Contributing to dubbo -Dubbo is released under the non-restrictive Apache 2.0 license, and follows a very standard Github development process, using Github tracker for issues and merging pull requests into master. If you want to contribute even something trivial please do not hesitate, but follow the guidelines below. +## Contributing to Dubbo +Dubbo is released under the non-restrictive Apache 2.0 licenses and follows a very standard Github development process, using Github tracker for issues and merging pull requests into master. Contributions of all form to this repository is acceptable, as long as it follows the prescribed community guidelines enumerated below. ### Sign the Contributor License Agreement -Before we accept a non-trivial patch or pull request we will need you to sign the Contributor License Agreement. Signing the contributor’s agreement does not grant anyone commit rights to the main repository, but it does mean that we can accept your contributions, and you will get an author credit if we do. Active contributors might be asked to join the core team, and given the ability to merge pull requests. +Before we accept a non-trivial patch or pull request (PRs), we will need you to sign the Contributor License Agreement. Signing the contributors' agreement does not grant anyone commits rights to the main repository, but it does mean that we can accept your contributions, and you will get an author credit if we do. Active contributors may get invited to join the core team that will grant them privileges to merge existing PRs. ### Contact #### Mailing list -The mailing list is the recommended way for discussing almost anything that related to Dubbo. Please refer to this [guide](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide) for detailed documentation on how to subscribe. +The mailing list is the recommended way of pursuing a discussion on almost anything related to Dubbo. Please refer to this [guide](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide) for detailed documentation on how to subscribe. -- [dev@dubbo.apache.org](mailto:dev-subscribe@dubbo.apache.org): the develop mailing list, you can ask question here if you have encountered any problem when using or developing Dubbo. -- [commits@dubbo.apache.org](mailto:commits-subscribe@dubbo.apache.org): all the commits will be sent to this mailing list. You can subscribe to it if you are interested in Dubbo's development. +- [dev@dubbo.apache.org](mailto:dev-subscribe@dubbo.apache.org): the developer mailing list where you can ask questions about an issue you may have encountered while working with Dubbo. +- [commits@dubbo.apache.org](mailto:commits-subscribe@dubbo.apache.org): the commit updates will get broadcasted on this mailing list. You can subscribe to it, should you be interested in following Dubbo's development. - [notifications@dubbo.apache.org](mailto:notifications-subscribe@dubbo.apache.org): all the Github [issue](https://github.com/apache/dubbo/issues) updates and [pull request](https://github.com/apache/dubbo/pulls) updates will be sent to this mailing list. ### Reporting issue @@ -21,33 +21,33 @@ Please follow the [template](https://github.com/apache/dubbo/issues/new?templat ### Code Conventions Our code style is almost in line with the standard java conventions (Popular IDE's default setting satisfy this), with the following additional restricts: -* If there are more than 120 characters in current line, start a new line. +* If there are more than 120 characters in the current line, begin a new line. -* Make sure all new .java files to have a simple Javadoc class comment with at least a @date tag identifying birth, and preferably at least a paragraph on what the class is for. +* Make sure all new .java files to have a simple Javadoc class comment with at least a @date tag identifying birth, and preferably at least a paragraph on the intended purpose of the class. * Add the ASF license header comment to all new .java files (copy from existing files in the project) -* Make sure no @author tag added to the file you contribute since @author tag is not used at Apache, other ways such as cvs will record all your contributions fairly. +* Make sure no @author tag gets appended to the file you contribute to as the @author tag is incompatible with Apache. Rest assured, other ways, including CVS, will ensure transparency, fairness in recording your contributions. * Add some Javadocs and, if you change the namespace, some XSD doc elements. -* A few unit tests should be added for a new feature or an important bugfix. +* Sufficient unit-tests should accompany new feature development or non-trivial bug fixes. -* If no-one else is using your branch, please rebase it against the current master (or other target branch in the main project). +* If no-one else is using your branch, please rebase it against the current master (or another target branch in the main project). -* When writing a commit message please follow these conventions, if you are fixing an existing issue please add Fixes #XXX at the end of the commit message (where XXX is the issue number). +* When writing a commit message, please follow the following conventions: should your commit address an open issue, please add Fixes #XXX at the end of the commit message (where XXX is the issue number). ### Contribution flow -This is a rough outline of what a contributor's workflow looks like: +A rough outline of an ideal contributors' workflow is as follows: * Fork the current repository -* Create a topic branch from where to base the contribution. This is usually master. +* Create a topic branch from where to base the contribution. Mostly, it's the master branch. * Make commits of logical units. -* Make sure commit messages are in the proper format (see below). +* Make sure the commit messages are in the proper format (see below). * Push changes in a topic branch to your forked repository. * Follow the checklist in the [pull request template](https://github.com/apache/dubbo/blob/master/PULL_REQUEST_TEMPLATE.md) -* Before you sending out the pull request, please sync your forked repository with remote repository, this will make your pull request simple and clear. See guide below: +* Before sending out the pull request, please sync your forked repository with the remote repository to ensure that your PR is elegant, concise. Reference the guide below: ``` git remote add upstream git@github.com:apache/dubbo.git git fetch upstream @@ -62,15 +62,15 @@ Thanks for contributing! ### Code style -We provide a template file [dubbo_codestyle_for_idea.xml](https://github.com/apache/dubbo/tree/master/codestyle/dubbo_codestyle_for_idea.xml) for IntelliJ idea, you can import it to you IDE. -If you use Eclipse you can config manually by referencing the same file. +We provide a template file [dubbo_codestyle_for_idea.xml](https://github.com/apache/dubbo/tree/master/codestyle/dubbo_codestyle_for_idea.xml) for IntelliJ idea that you can import it to your workplace. +If you use Eclipse, you can use the IntelliJ Idea template for manually configuring your file. **NOTICE** -It is very important to set the dubbo_codestyle_for_idea.xml, otherwise you will fail to pass the Travis CI. Steps to set the code style are as below: +It's critical to set the dubbo_codestyle_for_idea.xml to avoid the failure of your Travis CI builds. Steps to configure the code styles are as follows: 1. Enter `Editor > Code Style` -2. To manage a code style scheme, in the Code Style page, select the desired scheme from the drop-down list, and click ![manage profiles](codestyle/manage_profiles.png). -From the drop-down list, select `Import Scheme`, then select this option `IntelliJ IDEA code style XML` to import scheme +2. To manage a code style scheme, in the Code Style page, select the desired scheme from the drop-down list, and click on ![manage profiles](codestyle/manage_profiles.png). +From the drop-down list, select `Import Scheme`, then choose the option `IntelliJ IDEA code style XML` to import the scheme. 3. In the Scheme field, type the name of the new scheme and press ⏎ to save the changes. diff --git a/NOTICE b/NOTICE index 9664603fdd..e414ebfbe5 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Apache Dubbo -Copyright 2018-2020 The Apache Software Foundation +Copyright 2018-2021 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 20c5854a2f..a1384f243a 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,18 +1,16 @@ ## What is the purpose of the change -XXXXX + ## Brief changelog -XXXXX ## Verifying this change -XXXXX -Follow this checklist to help us incorporate your contribution quickly and easily: + -- [x] Make sure there is a [GITHUB_issue](https://github.com/apache/dubbo/issues) field for the change (usually before you start working on it). Trivial changes like typos do not require a GITHUB issue. Your pull request should address just this issue, without pulling in other changes - one PR resolves one issue. +- [x] Make sure there is a [GitHub_issue](https://github.com/apache/dubbo/issues) field for the change (usually before you start working on it). Trivial changes like typos do not require a GitHub issue. Your pull request should address just this issue, without pulling in other changes - one PR resolves one issue. - [ ] Format the pull request title like `[Dubbo-XXX] Fix UnknownException when host config not exist #XXX`. Each commit in the pull request should have a meaningful subject line and body. - [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. - [ ] Write necessary unit-test to verify your logic correction, more mock a little better when cross module dependency exist. If the new feature or significant change is committed, please remember to add sample in [dubbo samples](https://github.com/apache/dubbo-samples) project. diff --git a/README.md b/README.md index 153121d2fc..d84a5ecdf8 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,22 @@ # Apache Dubbo Project -[![Build Status](https://travis-ci.org/apache/dubbo.svg?branch=master)](https://travis-ci.org/apache/dubbo) -[![codecov](https://codecov.io/gh/apache/dubbo/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/dubbo) -![maven](https://img.shields.io/maven-central/v/org.apache.dubbo/dubbo.svg) -![license](https://img.shields.io/github/license/alibaba/dubbo.svg) +[![Build Status](https://travis-ci.com/apache/dubbo.svg?branch=master)](https://travis-ci.com/apache/dubbo) +[![Codecov](https://codecov.io/gh/apache/dubbo/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/dubbo) +![Maven](https://img.shields.io/maven-central/v/org.apache.dubbo/dubbo.svg) +![License](https://img.shields.io/github/license/alibaba/dubbo.svg) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/apache/dubbo.svg)](http://isitmaintained.com/project/apache/dubbo "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/apache/dubbo.svg)](http://isitmaintained.com/project/apache/dubbo "Percentage of issues still open") [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Apache%20Dubbo%20is%20a%20high-performance%2C%20java%20based%2C%20open%20source%20RPC%20framework.&url=http://dubbo.apache.org/&via=ApacheDubbo&hashtags=rpc,java,dubbo,micro-service) -[![](https://img.shields.io/twitter/follow/ApacheDubbo.svg?label=Follow&style=social&logoWidth=0)](https://twitter.com/intent/follow?screen_name=ApacheDubbo) +[![Twitter Follow](https://img.shields.io/twitter/follow/ApacheDubbo.svg?label=Follow&style=social&logoWidth=0)](https://twitter.com/intent/follow?screen_name=ApacheDubbo) [![Gitter](https://badges.gitter.im/alibaba/dubbo.svg)](https://gitter.im/alibaba/dubbo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) -Apache Dubbo is a high-performance, Java based open source RPC framework. Please visit [official site](http://dubbo.apache.org) for quick start and documentations, as well as [Wiki](https://github.com/apache/dubbo/wiki) for news, FAQ, and release notes. +Apache Dubbo is a high-performance, Java-based open-source RPC framework. Please visit the [official site](http://dubbo.apache.org) for the quick start guide and documentation, as well as the [wiki](https://github.com/apache/dubbo/wiki) for news, FAQ, and release notes. -We are now collecting dubbo user info in order to help us to improve Dubbo better, pls. kindly help us by providing yours on [issue#1012: Wanted: who's using dubbo](https://github.com/apache/dubbo/issues/1012), thanks :) +We are now collecting Dubbo user info to help us to improve Dubbo further. Kindly support us by providing your usage information on [issue#1012: Wanted: who's using dubbo](https://github.com/apache/dubbo/issues/1012), thanks :) ## Architecture -![Architecture](http://dubbo.apache.org/img/architecture.png) +![Architecture](https://dubbo.apache.org/imgs/architecture.png) ## Features @@ -29,14 +29,14 @@ We are now collecting dubbo user info in order to help us to improve Dubbo bette ## Getting started -The following code snippet comes from [Dubbo Samples](https://github.com/apache/dubbo-samples/tree/master/java/dubbo-samples-api). You may clone the sample project and step into `dubbo-samples-api` sub directory before read on. +The following code snippet comes from [Dubbo Samples](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-api). You may clone the sample project and step into the `dubbo-samples-api` subdirectory before proceeding. ```bash # git clone https://github.com/apache/dubbo-samples.git -# cd dubbo-samples/java/dubbo-samples-api +# cd dubbo-samples/dubbo-samples-api ``` -There's a [README](https://github.com/apache/dubbo-samples/tree/master/java/dubbo-samples-api/README.md) file under `dubbo-samples-api` directory. Read it and try this sample out by following the instructions. +There's a [README](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-api/README.md) file under `dubbo-samples-api` directory. We recommend referencing the samples in that directory by following the below-mentioned instructions: ### Maven dependency @@ -44,7 +44,7 @@ There's a [README](https://github.com/apache/dubbo-samples/tree/master/java/dubb 2.7.8 - + org.apache.dubbo @@ -70,7 +70,7 @@ public interface GreetingsService { } ``` -*See [api/GreetingsService.java](https://github.com/apache/dubbo-samples/blob/master/java/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/api/GreetingsService.java) on GitHub.* +*See [api/GreetingsService.java](https://github.com/apache/dubbo-samples/blob/master/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/api/GreetingsService.java) on GitHub.* ### Implement service interface for the provider @@ -87,7 +87,7 @@ public class GreetingsServiceImpl implements GreetingsService { } ``` -*See [provider/GreetingsServiceImpl.java](https://github.com/apache/dubbo-samples/blob/master/java/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/provider/GreetingsServiceImpl.java) on GitHub.* +*See [provider/GreetingsServiceImpl.java](https://github.com/apache/dubbo-samples/blob/master/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/provider/GreetingsServiceImpl.java) on GitHub.* ### Start service provider @@ -119,7 +119,7 @@ public class Application { } ``` -*See [provider/Application.java](https://github.com/apache/dubbo-samples/blob/master/java/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/provider/Application.java) on GitHub.* +*See [provider/Application.java](https://github.com/apache/dubbo-samples/blob/master/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/provider/Application.java) on GitHub.* ### Build and run the provider @@ -128,7 +128,7 @@ public class Application { # mvn -Djava.net.preferIPv4Stack=true -Dexec.mainClass=org.apache.dubbo.samples.provider.Application exec:java ``` -### Call remote service in consumer +### Call remote service in the consumer ```java package org.apache.dubbo.samples.client; @@ -153,7 +153,7 @@ public class Application { } } ``` -*See [consumer/Application.java](https://github.com/apache/dubbo-samples/blob/master/java/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/client/Application.java) on GitHub.* +*See [consumer/Application.java](https://github.com/apache/dubbo-samples/blob/master/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/client/Application.java) on GitHub.* ### Build and run the consumer @@ -167,14 +167,14 @@ The consumer will print out `hi, dubbo` on the screen. ### Next steps -* [Your first Dubbo application](http://dubbo.apache.org/en-us/blog/dubbo-101.html) - A 101 tutorial to reveal more details, with the same code above. -* [Dubbo user manual](http://dubbo.apache.org/en-us/docs/user/preface/background.html) - How to use Dubbo and all its features. -* [Dubbo developer guide](http://dubbo.apache.org/en-us/docs/dev/build.html) - How to involve in Dubbo development. -* [Dubbo admin manual](http://dubbo.apache.org/en-us/docs/admin/install/provider-demo.html) - How to admin and manage Dubbo services. +* [Your first Dubbo application](http://dubbo.apache.org/blog/2018/08/07/dubbo-101/) - A 101 tutorial to reveal more details, with the same code above. +* [Dubbo user manual](http://dubbo.apache.org/docs/v2.7/user/preface/background/) - How to use Dubbo and all its features. +* [Dubbo developer guide](http://dubbo.apache.org/docs/v2.7/dev/build/) - How to involve in Dubbo development. +* [Dubbo admin manual](http://dubbo.apache.org/docs/v2.7/admin/install/provider-demo/) - How to admin and manage Dubbo services. ## Building -If you want to try out the cutting-edge features, you can build with the following commands. (Java 1.8 is required to build the master branch) +If you want to try out the cutting-edge features, you can build with the following commands. (Java 1.8 is needed to build the master branch) ``` mvn clean install @@ -182,11 +182,11 @@ If you want to try out the cutting-edge features, you can build with the followi ## Contact -* Mailing list: +* Mailing list: * dev list: for dev/user discussion. [subscribe](mailto:dev-subscribe@dubbo.apache.org), [unsubscribe](mailto:dev-unsubscribe@dubbo.apache.org), [archive](https://lists.apache.org/list.html?dev@dubbo.apache.org), [guide](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide) - + * Bugs: [Issues](https://github.com/apache/dubbo/issues/new?template=dubbo-issue-report-template.md) -* Gitter: [Gitter channel](https://gitter.im/alibaba/dubbo) +* Gitter: [Gitter channel](https://gitter.im/alibaba/dubbo) * Twitter: [@ApacheDubbo](https://twitter.com/ApacheDubbo) ## Contributing @@ -195,18 +195,18 @@ See [CONTRIBUTING](https://github.com/apache/dubbo/blob/master/CONTRIBUTING.md) ### How can I contribute? -* Take a look at issues with tag called [`Good first issue`](https://github.com/apache/dubbo/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) or [`Help wanted`](https://github.com/apache/dubbo/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22). -* Join the discussion on mailing list, subscription [guide](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide). +* Take a look at issues with tags marked [`Good first issue`](https://github.com/apache/dubbo/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) or [`Help wanted`](https://github.com/apache/dubbo/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22). +* Join the discussion on the mailing list, subscription [guide](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide). * Answer questions on [issues](https://github.com/apache/dubbo/issues). -* Fix bugs reported on [issues](https://github.com/apache/dubbo/issues), and send us pull request. +* Fix bugs reported on [issues](https://github.com/apache/dubbo/issues), and send us a pull request. * Review the existing [pull request](https://github.com/apache/dubbo/pulls). * Improve the [website](https://github.com/apache/dubbo-website), typically we need * blog post * translation on documentation - * use cases about how Dubbo is being used in enterprise system. + * use cases around the integration of Dubbo in enterprise systems. * Improve the [dubbo-admin/dubbo-monitor](https://github.com/apache/dubbo-admin). * Contribute to the projects listed in [ecosystem](https://github.com/dubbo). -* Any form of contribution that is not mentioned above. +* Other forms of contribution not explicitly enumerated above. * If you would like to contribute, please send an email to dev@dubbo.apache.org to let us know! ## Reporting bugs @@ -215,7 +215,7 @@ Please follow the [template](https://github.com/apache/dubbo/issues/new?template ## Reporting a security vulnerability -Please report security vulnerability to [us](mailto:security@dubbo.apache.org) privately. +Please report security vulnerabilities to [us](mailto:security@dubbo.apache.org) privately. ## Dubbo ecosystem @@ -228,12 +228,12 @@ Please report security vulnerability to [us](mailto:security@dubbo.apache.org) p #### Language +* [Go](https://github.com/dubbo/dubbo-go) (recommended) * [Node.js](https://github.com/apache/dubbo-js) * [Python](https://github.com/dubbo/py-client-for-apache-dubbo) * [PHP](https://github.com/apache/dubbo-php-framework) -* [Go](https://github.com/dubbo/dubbo-go) * [Erlang](https://github.com/apache/dubbo-erlang) ## License -Apache Dubbo is under the Apache 2.0 license. See the [LICENSE](https://github.com/apache/dubbo/blob/master/LICENSE) file for details. +Apache Dubbo software is licenced under the Apache License Version 2.0. See the [LICENSE](https://github.com/apache/dubbo/blob/master/LICENSE) file for details. diff --git a/SECURITY.md b/SECURITY.md index ac0c2ad225..b8b2a2cae8 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,7 +2,7 @@ ## Supported Versions -Below is a table that shows versions that accept security fix. +Below is a table that shows versions that we accept security fixes. | Version | Supported | | ------- | ------------------ | @@ -15,11 +15,11 @@ Below is a table that shows versions that accept security fix. The Apache Software Foundation takes a rigorous standpoint in annihilating the security issues in its software projects. Apache Dubbo is highly sensitive and forthcoming to issues pertaining to its features and functionality. -If you have apprehensions regarding Dubbo's security or you discover vulnerability or potential threat, don’t hesitate to get in touch with the Apache Dubbo Security Team by dropping a mail at security@dubbo.apache.org. In the mail, specify the description of the issue or potential threat. You are also urged to recommend the way to reproduce and replicate the issue. The Dubbo community will get back to you after assessing and analysing the findings. +If you have apprehensions regarding Dubbo's security or you discover vulnerability or potential threat, don’t hesitate to get in touch with the Apache Dubbo Security Team by dropping a mail at security@dubbo.apache.org. In the email, specify the description of the issue or potential threat. You are also urged to recommend the way to reproduce and replicate the issue. The Dubbo community will get back to you after assessing and analysing the findings. PLEASE PAY ATTENTION to report the security issue on the security email before disclosing it on public domain. -## VULNERABILITY HANDLING +## Vulnerability Handling An overview of the vulnerability handling process is: diff --git a/dubbo-cluster/pom.xml b/dubbo-cluster/pom.xml index 3055454d9e..26b1295ffb 100644 --- a/dubbo-cluster/pom.xml +++ b/dubbo-cluster/pom.xml @@ -54,5 +54,11 @@ zookeeper test + + org.apache.dubbo + dubbo-serialization-hessian2 + ${project.parent.version} + test + \ No newline at end of file diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Cluster.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Cluster.java index 948827a172..1fdadf8b21 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Cluster.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Cluster.java @@ -22,7 +22,6 @@ import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; -import org.apache.dubbo.rpc.cluster.support.FailoverCluster; /** * Cluster. (SPI, Singleton, ThreadSafe) @@ -33,7 +32,8 @@ import org.apache.dubbo.rpc.cluster.support.FailoverCluster; */ @SPI(Cluster.DEFAULT) public interface Cluster { - String DEFAULT = FailoverCluster.NAME; + + String DEFAULT = "failover"; /** * Merge the directory invokers to a virtual invoker. @@ -56,4 +56,4 @@ public interface Cluster { } return ExtensionLoader.getExtensionLoader(Cluster.class).getExtension(name, wrap); } -} \ No newline at end of file +} diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Constants.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Constants.java index 03211133b9..b9935fc530 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Constants.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Constants.java @@ -106,4 +106,10 @@ public interface Constants { String PEER_KEY = "peer"; String CONSUMER_URL_KEY = "CONSUMER_URL"; + + /** + * prefix of arguments router key + */ + String ARGUMENTS = "arguments"; + } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java index a895677e33..f2310365d1 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java @@ -16,16 +16,6 @@ */ package org.apache.dubbo.rpc.cluster; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - import org.apache.dubbo.common.URL; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.extension.ExtensionLoader; @@ -44,6 +34,18 @@ import org.apache.dubbo.rpc.cluster.router.state.RouterCache; import org.apache.dubbo.rpc.cluster.router.state.StateRouter; import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import static org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY; + /** * Router chain */ @@ -85,7 +87,7 @@ public class RouterChain { private RouterChain(URL url) { loopPool = executorRepository.nextExecutorExecutor(); List extensionFactories = ExtensionLoader.getExtensionLoader(RouterFactory.class) - .getActivateExtension(url, "router"); + .getActivateExtension(url, ROUTER_KEY); List routers = extensionFactories.stream() .map(factory -> factory.getRouter(url)) diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/AbstractConfigurator.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/AbstractConfigurator.java index 4f27b084e4..751d284142 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/AbstractConfigurator.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/AbstractConfigurator.java @@ -47,6 +47,8 @@ import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY; */ public abstract class AbstractConfigurator implements Configurator { + private static final String TILDE = "~"; + private final URL configuratorUrl; public AbstractConfigurator(URL url) { @@ -76,7 +78,8 @@ public abstract class AbstractConfigurator implements Configurator { String configuratorSide = configuratorUrl.getSide(); if (currentSide.equals(configuratorSide) && CONSUMER.equals(configuratorSide) && 0 == configuratorUrl.getPort()) { url = configureIfMatch(NetUtils.getLocalHost(), url); - } else if (currentSide.equals(configuratorSide) && PROVIDER.equals(configuratorSide) && url.getPort() == configuratorUrl.getPort()) { + } else if (currentSide.equals(configuratorSide) && PROVIDER.equals(configuratorSide) && + url.getPort() == configuratorUrl.getPort()) { url = configureIfMatch(url.getHost(), url); } } @@ -137,10 +140,13 @@ public abstract class AbstractConfigurator implements Configurator { for (Map.Entry entry : configuratorUrl.getParameters().entrySet()) { String key = entry.getKey(); String value = entry.getValue(); - if (key.startsWith("~") || APPLICATION_KEY.equals(key) || SIDE_KEY.equals(key)) { - conditionKeys.add(key); + boolean startWithTilde = startWithTilde(key); + if (startWithTilde || APPLICATION_KEY.equals(key) || SIDE_KEY.equals(key)) { + if (startWithTilde) { + conditionKeys.add(key); + } if (value != null && !ANY_VALUE.equals(value) - && !value.equals(url.getParameter(key.startsWith("~") ? key.substring(1) : key))) { + && !value.equals(url.getParameter(startWithTilde ? key.substring(1) : key))) { return url; } } @@ -152,6 +158,13 @@ public abstract class AbstractConfigurator implements Configurator { return url; } + private boolean startWithTilde(String key) { + if (StringUtils.isNotEmpty(key) && key.startsWith(TILDE)) { + return true; + } + return false; + } + protected abstract URL doConfigure(URL currentUrl, URL configUrl); } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java index 26b907dcd2..76aaf396da 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java @@ -90,12 +90,14 @@ public class ConfigParser { parseEnabled(item, config, urlBuilder); - urlBuilder.append("&category=").append(DYNAMIC_CONFIGURATORS_CATEGORY); urlBuilder.append("&configVersion=").append(config.getConfigVersion()); List apps = item.getApplications(); if (CollectionUtils.isNotEmpty(apps)) { - apps.forEach(app -> urls.add(URL.valueOf(urlBuilder.append("&application=").append(app).toString()))); + apps.forEach(app -> { + StringBuilder tmpUrlBuilder = new StringBuilder(urlBuilder); + urls.add(URL.valueOf(tmpUrlBuilder.append("&application=").append(app).toString())); + }); } else { urls.add(URL.valueOf(urlBuilder.toString())); } @@ -118,17 +120,18 @@ public class ConfigParser { services.add("*"); } for (String s : services) { - urlBuilder.append(appendService(s)); - urlBuilder.append(toParameterString(item)); + StringBuilder tmpUrlBuilder = new StringBuilder(urlBuilder); + tmpUrlBuilder.append(appendService(s)); + tmpUrlBuilder.append(toParameterString(item)); - urlBuilder.append("&application=").append(config.getKey()); + tmpUrlBuilder.append("&application=").append(config.getKey()); - parseEnabled(item, config, urlBuilder); + parseEnabled(item, config, tmpUrlBuilder); - urlBuilder.append("&category=").append(APP_DYNAMIC_CONFIGURATORS_CATEGORY); - urlBuilder.append("&configVersion=").append(config.getConfigVersion()); + tmpUrlBuilder.append("&category=").append(APP_DYNAMIC_CONFIGURATORS_CATEGORY); + tmpUrlBuilder.append("&configVersion=").append(config.getConfigVersion()); - urls.add(URL.valueOf(urlBuilder.toString())); + urls.add(URL.valueOf(tmpUrlBuilder.toString())); } } return urls; diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/governance/GovernanceRuleRepository.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/governance/GovernanceRuleRepository.java index a8e5e18ac4..aaa604f2ff 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/governance/GovernanceRuleRepository.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/governance/GovernanceRuleRepository.java @@ -35,7 +35,7 @@ public interface GovernanceRuleRepository { } - /* + /** * {@link #removeListener(String, String, ConfigurationListener)} * * @param key the key to represent a configuration diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/ConsistentHashLoadBalance.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/ConsistentHashLoadBalance.java index 855521f2b3..4f5859de2d 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/ConsistentHashLoadBalance.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/ConsistentHashLoadBalance.java @@ -17,13 +17,11 @@ package org.apache.dubbo.rpc.cluster.loadbalance; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.io.Bytes; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.support.RpcUtils; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -88,7 +86,7 @@ public class ConsistentHashLoadBalance extends AbstractLoadBalance { for (Invoker invoker : invokers) { String address = invoker.getUrl().getAddress(); for (int i = 0; i < replicaNumber / 4; i++) { - byte[] digest = md5(address + i); + byte[] digest = Bytes.getMD5(address + i); for (int h = 0; h < 4; h++) { long m = hash(digest, h); virtualInvokers.put(m, invoker); @@ -99,7 +97,7 @@ public class ConsistentHashLoadBalance extends AbstractLoadBalance { public Invoker select(Invocation invocation) { String key = toKey(invocation.getArguments()); - byte[] digest = md5(key); + byte[] digest = Bytes.getMD5(key); return selectForKey(hash(digest, 0)); } @@ -128,20 +126,6 @@ public class ConsistentHashLoadBalance extends AbstractLoadBalance { | (digest[number * 4] & 0xFF)) & 0xFFFFFFFFL; } - - private byte[] md5(String value) { - MessageDigest md5; - try { - md5 = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException(e.getMessage(), e); - } - md5.reset(); - byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - md5.update(bytes); - return md5.digest(); - } - } } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java index adfd04b316..50215d699b 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java @@ -49,20 +49,17 @@ public class RandomLoadBalance extends AbstractLoadBalance { int length = invokers.size(); // Every invoker has the same weight? boolean sameWeight = true; - // the weight of every invokers + // the maxWeight of every invokers, the minWeight = 0 or the maxWeight of the last invoker int[] weights = new int[length]; - // the first invoker's weight - int firstWeight = getWeight(invokers.get(0), invocation); - weights[0] = firstWeight; // The sum of weights - int totalWeight = firstWeight; - for (int i = 1; i < length; i++) { + int totalWeight = 0; + for (int i = 0; i < length; i++) { int weight = getWeight(invokers.get(i), invocation); - // save for later use - weights[i] = weight; // Sum totalWeight += weight; - if (sameWeight && weight != firstWeight) { + // save for later use + weights[i] = totalWeight; + if (sameWeight && totalWeight != weight * (i + 1)) { sameWeight = false; } } @@ -71,8 +68,7 @@ public class RandomLoadBalance extends AbstractLoadBalance { int offset = ThreadLocalRandom.current().nextInt(totalWeight); // Return a invoker based on the random value. for (int i = 0; i < length; i++) { - offset -= weights[i]; - if (offset < 0) { + if (offset < weights[i]) { return invokers.get(i); } } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouter.java index dbf32fde8d..f6cfec2993 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouter.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouter.java @@ -26,6 +26,7 @@ import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.cluster.Constants; import org.apache.dubbo.rpc.cluster.router.AbstractRouter; import java.text.ParseException; @@ -50,13 +51,18 @@ import static org.apache.dubbo.rpc.cluster.Constants.RUNTIME_KEY; /** * ConditionRouter - * + * It supports the conditional routing configured by "override://", in 2.6.x, + * refer to https://dubbo.apache.org/en/docs/v2.7/user/examples/routing-rule/ . + * For 2.7.x and later, please refer to {@link org.apache.dubbo.rpc.cluster.router.condition.config.ServiceRouter} + * and {@link org.apache.dubbo.rpc.cluster.router.condition.config.AppRouter} + * refer to https://dubbo.apache.org/zh/docs/v2.7/user/examples/routing-rule/ . */ public class ConditionRouter extends AbstractRouter { public static final String NAME = "condition"; private static final Logger logger = LoggerFactory.getLogger(ConditionRouter.class); protected static final Pattern ROUTE_PATTERN = Pattern.compile("([&!=,]*)\\s*([^&!=,\\s]+)"); + protected static Pattern ARGUMENTS_PATTERN = Pattern.compile("arguments\\[([0-9]+)\\]"); protected Map whenCondition; protected Map thenCondition; @@ -65,7 +71,9 @@ public class ConditionRouter extends AbstractRouter { public ConditionRouter(String rule, boolean force, boolean enabled) { this.force = force; this.enabled = enabled; - this.init(rule); + if (enabled) { + this.init(rule); + } } public ConditionRouter(URL url) { @@ -73,7 +81,9 @@ public class ConditionRouter extends AbstractRouter { this.priority = url.getParameter(PRIORITY_KEY, 0); this.force = url.getParameter(FORCE_KEY, false); this.enabled = url.getParameter(ENABLED_KEY, true); - init(url.getParameterAndDecoded(RULE_KEY)); + if (enabled) { + init(url.getParameterAndDecoded(RULE_KEY)); + } } public void init(String rule) { @@ -226,6 +236,16 @@ public class ConditionRouter extends AbstractRouter { boolean result = false; for (Map.Entry matchPair : condition.entrySet()) { String key = matchPair.getKey(); + + if (key.startsWith(Constants.ARGUMENTS)) { + if (!matchArguments(matchPair, invocation)) { + return false; + } else { + result = true; + continue; + } + } + String sampleValue; //get real invoked method name from invocation if (invocation != null && (METHOD_KEY.equals(key) || METHODS_KEY.equals(key))) { @@ -258,6 +278,45 @@ public class ConditionRouter extends AbstractRouter { return result; } + /** + * analysis the arguments in the rule. + * Examples would be like this: + * "arguments[0]=1", whenCondition is that the first argument is equal to '1'. + * "arguments[1]=a", whenCondition is that the second argument is equal to 'a'. + * @param matchPair + * @param invocation + * @return + */ + public boolean matchArguments(Map.Entry matchPair, Invocation invocation) { + try { + // split the rule + String key = matchPair.getKey(); + String[] expressArray = key.split("\\."); + String argumentExpress = expressArray[0]; + final Matcher matcher = ARGUMENTS_PATTERN.matcher(argumentExpress); + if (!matcher.find()) { + return false; + } + + //extract the argument index + int index = Integer.parseInt(matcher.group(1)); + if (index < 0 || index > invocation.getArguments().length) { + return false; + } + + //extract the argument value + Object object = invocation.getArguments()[index]; + + if (matchPair.getValue().isMatch(String.valueOf(object), null)) { + return true; + } + } catch (Exception e) { + logger.warn("Arguments match failed, matchPair[]" + matchPair + "] invocation[" + invocation + "]", e); + } + + return false; + } + protected static final class MatchPair { final Set matches = new HashSet(); final Set mismatches = new HashSet(); diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterFactory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterFactory.java index a616d8d015..7701c4c62e 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterFactory.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterFactory.java @@ -22,7 +22,7 @@ import org.apache.dubbo.rpc.cluster.RouterFactory; /** * ConditionRouterFactory - * + * Load when "override://" is configured {@link ConditionRouter} */ public class ConditionRouterFactory implements RouterFactory { diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java index 690ebf9bcb..b0bfdc6959 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java @@ -33,7 +33,6 @@ import org.apache.dubbo.rpc.cluster.router.AbstractRouter; import org.apache.dubbo.rpc.cluster.router.tag.model.TagRouterRule; import org.apache.dubbo.rpc.cluster.router.tag.model.TagRuleParser; -import java.net.UnknownHostException; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -160,7 +159,7 @@ public class TagRouter extends AbstractRouter implements ConfigurationListener { * @return */ private List> filterUsingStaticTag(List> invokers, URL url, Invocation invocation) { - List> result = invokers; + List> result; // Dynamic param String tag = StringUtils.isEmpty(invocation.getAttachment(TAG_KEY)) ? url.getParameter(TAG_KEY) : invocation.getAttachment(TAG_KEY); @@ -188,19 +187,11 @@ public class TagRouter extends AbstractRouter implements ConfigurationListener { } private boolean isForceUseTag(Invocation invocation) { - return Boolean.valueOf(invocation.getAttachment(FORCE_USE_TAG, url.getParameter(FORCE_USE_TAG, "false"))); + return Boolean.parseBoolean(invocation.getAttachment(FORCE_USE_TAG, url.getParameter(FORCE_USE_TAG, "false"))); } private List> filterInvoker(List> invokers, Predicate> predicate) { - boolean filter = false; - for (int i = 0; i < invokers.size(); ++i) { - Invoker invoker = invokers.get(i); - if (!predicate.test(invoker)) { - filter = true; - break; - } - } - if (!filter) { + if (invokers.stream().allMatch(predicate)) { return invokers; } @@ -226,8 +217,6 @@ public class TagRouter extends AbstractRouter implements ConfigurationListener { if ((ANYHOST_VALUE + ":" + port).equals(address)) { return true; } - } catch (UnknownHostException e) { - logger.error("The format of ip address is invalid in tag route. Address :" + address, e); } catch (Exception e) { logger.error("The format of ip address is invalid in tag route. Address :" + address, e); } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java index 450fa9197b..268140a19b 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.rpc.cluster.support; +import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.Invocation; @@ -30,11 +31,13 @@ import java.util.List; /** * BroadcastClusterInvoker - * */ public class BroadcastClusterInvoker extends AbstractClusterInvoker { private static final Logger logger = LoggerFactory.getLogger(BroadcastClusterInvoker.class); + private static final String BROADCAST_FAIL_PERCENT_KEY = "broadcast.fail.percent"; + private static final int MAX_BROADCAST_FAIL_PERCENT = 100; + private static final int MIN_BROADCAST_FAIL_PERCENT = 0; public BroadcastClusterInvoker(Directory directory) { super(directory); @@ -47,21 +50,67 @@ public class BroadcastClusterInvoker extends AbstractClusterInvoker { RpcContext.getServiceContext().setInvokers((List) invokers); RpcException exception = null; Result result = null; + URL url = getUrl(); + // The value range of broadcast.fail.threshold must be 0~100. + // 100 means that an exception will be thrown last, and 0 means that as long as an exception occurs, it will be thrown. + // see https://github.com/apache/dubbo/pull/7174 + int broadcastFailPercent = url.getParameter(BROADCAST_FAIL_PERCENT_KEY, MAX_BROADCAST_FAIL_PERCENT); + + if (broadcastFailPercent < MIN_BROADCAST_FAIL_PERCENT || broadcastFailPercent > MAX_BROADCAST_FAIL_PERCENT) { + logger.info(String.format("The value corresponding to the broadcast.fail.percent parameter must be between 0 and 100. " + + "The current setting is %s, which is reset to 100.", broadcastFailPercent)); + broadcastFailPercent = MAX_BROADCAST_FAIL_PERCENT; + } + + int failThresholdIndex = invokers.size() * broadcastFailPercent / MAX_BROADCAST_FAIL_PERCENT; + int failIndex = 0; for (Invoker invoker : invokers) { try { result = invokeWithContext(invoker, invocation); - } catch (RpcException e) { - exception = e; - logger.warn(e.getMessage(), e); + if (null != result && result.hasException()) { + Throwable resultException = result.getException(); + if (null != resultException) { + exception = getRpcException(result.getException()); + logger.warn(exception.getMessage(), exception); + if (failIndex == failThresholdIndex) { + break; + } else { + failIndex++; + } + } + } } catch (Throwable e) { - exception = new RpcException(e.getMessage(), e); - logger.warn(e.getMessage(), e); + exception = getRpcException(e); + logger.warn(exception.getMessage(), exception); + if (failIndex == failThresholdIndex) { + break; + } else { + failIndex++; + } } } + if (exception != null) { + if (failIndex == failThresholdIndex) { + logger.debug( + String.format("The number of BroadcastCluster call failures has reached the threshold %s", failThresholdIndex)); + } else { + logger.debug(String.format("The number of BroadcastCluster call failures has not reached the threshold %s, fail size is %s", + failIndex)); + } throw exception; } + return result; } + private RpcException getRpcException(Throwable throwable) { + RpcException rpcException = null; + if (throwable instanceof RpcException) { + rpcException = (RpcException) throwable; + } else { + rpcException = new RpcException(throwable.getMessage(), throwable); + } + return rpcException; + } } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java index b7a30ed372..72def111ab 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java @@ -29,6 +29,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CORE_THREADS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX; import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INVOKER_LISTENER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY; @@ -90,6 +91,9 @@ public class ClusterUtils { if(map.containsKey(VERSION_KEY)){ copyOfLocalMap.remove(VERSION_KEY); } + if (map.containsKey(GENERIC_KEY)) { + copyOfLocalMap.remove(GENERIC_KEY); + } copyOfLocalMap.remove(RELEASE_KEY); copyOfLocalMap.remove(DUBBO_VERSION_KEY); @@ -99,20 +103,22 @@ public class ClusterUtils { map.putAll(copyOfLocalMap); - map.put(REMOTE_APPLICATION_KEY, remoteMap.get(APPLICATION_KEY)); + if (remoteMap != null) { + map.put(REMOTE_APPLICATION_KEY, remoteMap.get(APPLICATION_KEY)); - // Combine filters and listeners on Provider and Consumer - String remoteFilter = remoteMap.get(REFERENCE_FILTER_KEY); - String localFilter = copyOfLocalMap.get(REFERENCE_FILTER_KEY); - if (remoteFilter != null && remoteFilter.length() > 0 - && localFilter != null && localFilter.length() > 0) { - map.put(REFERENCE_FILTER_KEY, remoteFilter + "," + localFilter); - } - String remoteListener = remoteMap.get(INVOKER_LISTENER_KEY); - String localListener = copyOfLocalMap.get(INVOKER_LISTENER_KEY); - if (remoteListener != null && remoteListener.length() > 0 - && localListener != null && localListener.length() > 0) { - map.put(INVOKER_LISTENER_KEY, remoteListener + "," + localListener); + // Combine filters and listeners on Provider and Consumer + String remoteFilter = remoteMap.get(REFERENCE_FILTER_KEY); + String localFilter = copyOfLocalMap.get(REFERENCE_FILTER_KEY); + if (remoteFilter != null && remoteFilter.length() > 0 + && localFilter != null && localFilter.length() > 0) { + map.put(REFERENCE_FILTER_KEY, remoteFilter + "," + localFilter); + } + String remoteListener = remoteMap.get(INVOKER_LISTENER_KEY); + String localListener = copyOfLocalMap.get(INVOKER_LISTENER_KEY); + if (remoteListener != null && remoteListener.length() > 0 + && localListener != null && localListener.length() > 0) { + map.put(INVOKER_LISTENER_KEY, remoteListener + "," + localListener); + } } } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvoker.java index 414deaa0fc..a445d366b5 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvoker.java @@ -58,10 +58,7 @@ public class FailoverClusterInvoker extends AbstractClusterInvoker { List> copyInvokers = invokers; checkInvokers(copyInvokers, invocation); String methodName = RpcUtils.getMethodName(invocation); - int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1; - if (len <= 0) { - len = 1; - } + int len = calculateInvokeTimes(methodName); // retry loop. RpcException le = null; // last exception. List> invoked = new ArrayList>(copyInvokers.size()); // invoked invokers. @@ -113,4 +110,19 @@ public class FailoverClusterInvoker extends AbstractClusterInvoker { + le.getMessage(), le.getCause() != null ? le.getCause() : le); } + private int calculateInvokeTimes(String methodName) { + int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1; + RpcContext rpcContext = RpcContext.getClientAttachment(); + Object retry = rpcContext.getObjectAttachment(RETRIES_KEY); + if (retry instanceof Number) { + len = ((Number) retry).intValue() + 1; + rpcContext.removeAttachment(RETRIES_KEY); + } + if (len <= 0) { + len = 1; + } + + return len; + } + } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java index 7bc1f9429d..95a7bd2a7d 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java @@ -113,7 +113,7 @@ public class MergeableClusterInvoker extends AbstractClusterInvoker { if (resultList.isEmpty()) { return AsyncRpcResult.newDefaultAsyncResult(invocation); } else if (resultList.size() == 1) { - return resultList.iterator().next(); + return AsyncRpcResult.newDefaultAsyncResult(resultList.get(0).getValue(), invocation); } if (returnType == void.class) { diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareClusterInvoker.java index f5043e33f6..97b7a0b544 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareClusterInvoker.java @@ -33,7 +33,6 @@ import java.util.List; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.PREFERRED_KEY; -import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_ZONE; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_ZONE_FORCE; import static org.apache.dubbo.common.constants.RegistryConstants.ZONE_KEY; @@ -62,7 +61,7 @@ public class ZoneAwareClusterInvoker extends AbstractClusterInvoker { for (Invoker invoker : invokers) { ClusterInvoker clusterInvoker = (ClusterInvoker) invoker; if (clusterInvoker.isAvailable() && clusterInvoker.getRegistryUrl() - .getParameter(REGISTRY_KEY + "." + PREFERRED_KEY, false)) { + .getParameter(PREFERRED_KEY, false)) { return clusterInvoker.invoke(invocation); } } @@ -72,7 +71,7 @@ public class ZoneAwareClusterInvoker extends AbstractClusterInvoker { if (StringUtils.isNotEmpty(zone)) { for (Invoker invoker : invokers) { ClusterInvoker clusterInvoker = (ClusterInvoker) invoker; - if (clusterInvoker.isAvailable() && zone.equals(clusterInvoker.getRegistryUrl().getParameter(REGISTRY_KEY + "." + ZONE_KEY))) { + if (clusterInvoker.isAvailable() && zone.equals(clusterInvoker.getRegistryUrl().getParameter(ZONE_KEY))) { return clusterInvoker.invoke(invocation); } } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java index 1c2d047f78..684f372fb2 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java @@ -256,7 +256,7 @@ public abstract class AbstractCluster implements Cluster { @Override protected Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { - // The only purpose is to build a interceptor chain, so the cluster related logic doesn't matter. + // The only purpose is to build an interceptor chain, so the cluster related logic doesn't matter. return null; } } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java index a682297cdc..51d0a49755 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java @@ -162,7 +162,7 @@ public class MockClusterInvoker implements ClusterInvoker { /** * Return MockInvoker * Contract: - * directory.list() will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is present in invocation, otherwise, a list of mock invokers will return. + * directory.list() will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is absent or not true in invocation, otherwise, a list of mock invokers will return. * if directory.list() returns more than one mock invoker, only one of them will be used. * * @param invocation @@ -174,7 +174,7 @@ public class MockClusterInvoker implements ClusterInvoker { if (invocation instanceof RpcInvocation) { //Note the implicit contract (although the description is added to the interface declaration, but extensibility is a problem. The practice placed in the attachment needs to be improved) ((RpcInvocation) invocation).setAttachment(INVOCATION_NEED_MOCK, Boolean.TRUE.toString()); - //directory will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is present in invocation, otherwise, a list of mock invokers will return. + //directory will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is absent or not true in invocation, otherwise, a list of mock invokers will return. try { invokers = directory.list(invocation); } catch (RpcException e) { diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalanceTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalanceTest.java index 8d77aa4ad1..bb6260c967 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalanceTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalanceTest.java @@ -131,13 +131,7 @@ public class RoundRobinLoadBalanceTest extends LoadBalanceBaseTest { recycleTimeField = RoundRobinLoadBalance.class.getDeclaredField("RECYCLE_PERIOD"); recycleTimeField.setAccessible(true); recycleTimeField.setInt(RoundRobinLoadBalance.class, 10); - } catch (NoSuchFieldException e) { - Assertions.assertTrue(true, "getField failed"); - } catch (SecurityException e) { - Assertions.assertTrue(true, "getField failed"); - } catch (IllegalArgumentException e) { - Assertions.assertTrue(true, "getField failed"); - } catch (IllegalAccessException e) { + } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException | SecurityException e) { Assertions.assertTrue(true, "getField failed"); } } diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterTest.java index 204ce9adbf..4637976309 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterTest.java @@ -311,4 +311,38 @@ public class ConditionRouterTest { Assertions.assertEquals(0, filteredInvokers.size()); } + @Test + public void testRoute_Arguments() { + Router router = new ConditionRouterFactory().getRouter(getRouteUrl("arguments[0] = a " + " => " + " host = 1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true))); + List> invokers = new ArrayList<>(); + Invoker invoker1 = new MockInvoker<>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService")); + Invoker invoker2 = new MockInvoker<>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService")); + Invoker invoker3 = new MockInvoker<>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService")); + invokers.add(invoker1); + invokers.add(invoker2); + invokers.add(invoker3); + RpcInvocation invocation = new RpcInvocation(); + String p = "a"; + invocation.setArguments(new Object[]{null}); + List> fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation); + Assertions.assertEquals(3, fileredInvokers.size()); + + invocation.setArguments(new Object[]{p}); + fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation); + Assertions.assertEquals(0, fileredInvokers.size()); + + router = new ConditionRouterFactory().getRouter(getRouteUrl("arguments = b " + " => " + " host = 1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true))); + fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation); + Assertions.assertEquals(3, fileredInvokers.size()); + + router = new ConditionRouterFactory().getRouter(getRouteUrl("arguments[10].inner = a " + " => " + " host = 1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true))); + fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation); + Assertions.assertEquals(3, fileredInvokers.size()); + + int integer = 1; + invocation.setArguments(new Object[]{integer}); + router = new ConditionRouterFactory().getRouter(getRouteUrl("arguments[0].inner = 1 " + " => " + " host = 1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true))); + fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation); + Assertions.assertEquals(0, fileredInvokers.size()); + } } diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java index 613a527f34..a162526292 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java @@ -22,6 +22,7 @@ import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.cluster.Directory; @@ -142,6 +143,33 @@ public class FailoverClusterInvokerTest { } } + @Test() + public void testInvoke_retryTimes2() { + int finalRetries = 1; + given(invoker1.invoke(invocation)).willThrow(new RpcException(RpcException.TIMEOUT_EXCEPTION)); + given(invoker1.isAvailable()).willReturn(false); + given(invoker1.getUrl()).willReturn(url); + given(invoker1.getInterface()).willReturn(FailoverClusterInvokerTest.class); + + given(invoker2.invoke(invocation)).willThrow(new RpcException()); + given(invoker2.isAvailable()).willReturn(false); + given(invoker2.getUrl()).willReturn(url); + given(invoker2.getInterface()).willReturn(FailoverClusterInvokerTest.class); + + RpcContext rpcContext = RpcContext.getContext(); + rpcContext.setAttachment("retries", finalRetries); + + FailoverClusterInvoker invoker = new FailoverClusterInvoker(dic); + try { + Result ret = invoker.invoke(invocation); + assertSame(result, ret); + fail(); + } catch (RpcException expected) { + assertTrue((expected.isTimeout() || expected.getCode() == 0)); + assertTrue(expected.getMessage().indexOf((finalRetries+1) + " times") > 0); + } + } + @Test() public void testNoInvoke() { dic = mock(Directory.class); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/BaseServiceMetadata.java b/dubbo-common/src/main/java/org/apache/dubbo/common/BaseServiceMetadata.java index f8a6516f27..9c04f9ad39 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/BaseServiceMetadata.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/BaseServiceMetadata.java @@ -18,11 +18,13 @@ package org.apache.dubbo.common; import org.apache.dubbo.common.utils.StringUtils; +import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_VERSION; + /** * 2019-10-10 */ public class BaseServiceMetadata { - public static final char COLON_SEPERATOR = ':'; + public static final char COLON_SEPARATOR = ':'; protected String serviceKey; protected String serviceInterfaceName; @@ -48,7 +50,7 @@ public class BaseServiceMetadata { public static String versionFromServiceKey(String serviceKey) { int index = serviceKey.indexOf(":"); if (index == -1) { - return null; + return DEFAULT_VERSION; } return serviceKey.substring(index + 1); } @@ -77,7 +79,7 @@ public class BaseServiceMetadata { public String getDisplayServiceKey() { StringBuilder serviceNameBuilder = new StringBuilder(); serviceNameBuilder.append(serviceInterfaceName); - serviceNameBuilder.append(COLON_SEPERATOR).append(version); + serviceNameBuilder.append(COLON_SEPARATOR).append(version); return serviceNameBuilder.toString(); } @@ -88,7 +90,7 @@ public class BaseServiceMetadata { * @return */ public static BaseServiceMetadata revertDisplayServiceKey(String displayKey) { - String[] eles = StringUtils.split(displayKey, COLON_SEPERATOR); + String[] eles = StringUtils.split(displayKey, COLON_SEPARATOR); if (eles == null || eles.length < 1 || eles.length > 2) { return new BaseServiceMetadata(); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/Parameters.java b/dubbo-common/src/main/java/org/apache/dubbo/common/Parameters.java index 90e3415ed1..790c476234 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/Parameters.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/Parameters.java @@ -244,6 +244,9 @@ public class Parameters { @Override public boolean equals(Object o) { + if (this == o) { + return true; + } return parameters.equals(o); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java index 96383df57a..404fd725af 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java @@ -48,7 +48,7 @@ import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; -import static org.apache.dubbo.common.BaseServiceMetadata.COLON_SEPERATOR; +import static org.apache.dubbo.common.BaseServiceMetadata.COLON_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; @@ -1281,7 +1281,7 @@ class URL implements Serializable { return getServiceInterface(); } return getServiceInterface() + - COLON_SEPERATOR + getVersion(); + COLON_SEPARATOR + getVersion(); } /** diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java index 3ca12467ff..1b45b1f534 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java @@ -221,7 +221,7 @@ public final class URLStrParser { */ public static URL parseEncodedStr(String encodedURLStr, boolean modifiable) { Map parameters = null; - int pathEndIdx = encodedURLStr.indexOf("%3F");// '?' + int pathEndIdx = encodedURLStr.toUpperCase().indexOf("%3F");// '?' if (pathEndIdx >= 0) { parameters = parseEncodedParams(encodedURLStr, pathEndIdx + 3); } else { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java b/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java index c6610cb23a..0b3da94c9a 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java @@ -349,7 +349,7 @@ public final class JavaBeanSerializeUtil { } } - return cl.newInstance(); + return cl.getDeclaredConstructor().newInstance(); } public static Object getConstructorArg(Class cl) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/ClassGenerator.java b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/ClassGenerator.java index b81b7a395b..252c3aa719 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/ClassGenerator.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/ClassGenerator.java @@ -340,9 +340,7 @@ public final class ClassGenerator { return mCtc.toClass(loader, pd); } catch (RuntimeException e) { throw e; - } catch (NotFoundException e) { - throw new RuntimeException(e.getMessage(), e); - } catch (CannotCompileException e) { + } catch (NotFoundException | CannotCompileException e) { throw new RuntimeException(e.getMessage(), e); } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/CustomizedLoaderClassPath.java b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/CustomizedLoaderClassPath.java index 4afc8867e2..e5f639dd9e 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/CustomizedLoaderClassPath.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/CustomizedLoaderClassPath.java @@ -49,8 +49,9 @@ public class CustomizedLoaderClassPath implements ClassPath { public String toString() { Object cl = null; - if (clref != null) + if (clref != null) { cl = clref.get(); + } return cl == null ? "" : cl.toString(); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Mixin.java b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Mixin.java index f7a8423638..cd16d381ad 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Mixin.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Mixin.java @@ -174,7 +174,7 @@ public abstract class Mixin { ccm.setSuperClass(Mixin.class.getName()); ccm.addMethod("public Object newInstance(Object[] delegates){ return new " + micn + "($1); }"); Class mixin = ccm.toClass(); - return (Mixin) mixin.newInstance(); + return (Mixin) mixin.getDeclaredConstructor().newInstance(); } catch (RuntimeException e) { throw e; } catch (Exception e) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Proxy.java b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Proxy.java index c7fc5b2775..4c435a8f77 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Proxy.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Proxy.java @@ -187,9 +187,6 @@ public abstract class Proxy { if (worked.contains(desc) || Modifier.isStatic(method.getModifiers())) { continue; } - if (ics[i].isInterface() && Modifier.isStatic(method.getModifiers())) { - continue; - } worked.add(desc); int ix = methods.size(); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Wrapper.java b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Wrapper.java index 8f52a34fc4..11257e9cc1 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Wrapper.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Wrapper.java @@ -19,11 +19,15 @@ package org.apache.dubbo.common.bytecode; import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.ReflectUtils; +import javassist.ClassPool; +import javassist.CtMethod; + import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -31,6 +35,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Matcher; +import java.util.stream.Collectors; /** * Wrapper. @@ -113,7 +118,7 @@ public abstract class Wrapper { return OBJECT_WRAPPER; } - return WRAPPER_MAP.computeIfAbsent(c, key -> makeWrapper(key)); + return WRAPPER_MAP.computeIfAbsent(c, Wrapper::makeWrapper); } private static Wrapper makeWrapper(Class c) { @@ -150,10 +155,32 @@ public abstract class Wrapper { pts.put(fn, ft); } - Method[] methods = c.getMethods(); + final ClassPool classPool = new ClassPool(ClassPool.getDefault()); + classPool.appendClassPath(new CustomizedLoaderClassPath(cl)); + + List allMethod = new ArrayList<>(); + try { + final CtMethod[] ctMethods = classPool.get(c.getName()).getMethods(); + for (CtMethod method : ctMethods) { + allMethod.add(ReflectUtils.getDesc(method)); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + + Method[] methods = Arrays.stream(c.getMethods()) + .filter(method -> allMethod.contains(ReflectUtils.getDesc(method))) + .collect(Collectors.toList()) + .toArray(new Method[] {}); // get all public method. boolean hasMethod = hasMethods(methods); if (hasMethod) { + Map sameNameMethodCount = new HashMap<>((int) (methods.length / 0.75f) + 1); + for (Method m : methods) { + sameNameMethodCount.compute(m.getName(), + (key, oldValue) -> oldValue == null ? 1 : oldValue + 1); + } + c3.append(" try{"); for (Method m : methods) { //ignore Object's method. @@ -166,14 +193,8 @@ public abstract class Wrapper { int len = m.getParameterTypes().length; c3.append(" && ").append(" $3.length == ").append(len); - boolean override = false; - for (Method m2 : methods) { - if (m != m2 && m.getName().equals(m2.getName())) { - override = true; - break; - } - } - if (override) { + boolean overload = sameNameMethodCount.get(m.getName()) > 1; + if (overload) { if (len > 0) { for (int l = 0; l < len; l++) { c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"") @@ -226,7 +247,7 @@ public abstract class Wrapper { } } c1.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" field or setter method in class " + c.getName() + ".\"); }"); - c2.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" field or setter method in class " + c.getName() + ".\"); }"); + c2.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" field or getter method in class " + c.getName() + ".\"); }"); // make class long id = WRAPPER_CLASS_COUNTER.getAndIncrement(); @@ -263,7 +284,7 @@ public abstract class Wrapper { for (Method m : ms.values()) { wc.getField("mts" + ix++).set(null, m.getParameterTypes()); } - return (Wrapper) wc.newInstance(); + return (Wrapper) wc.getDeclaredConstructor().newInstance(); } catch (RuntimeException e) { throw e; } catch (Throwable e) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/ClassUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/ClassUtils.java index 365cb7f36d..d09afadcce 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/ClassUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/ClassUtils.java @@ -22,6 +22,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; @@ -48,10 +49,8 @@ public class ClassUtils { public static Object newInstance(String name) { try { - return forName(name).newInstance(); - } catch (InstantiationException e) { - throw new IllegalStateException(e.getMessage(), e); - } catch (IllegalAccessException e) { + return forName(name).getDeclaredConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new IllegalStateException(e.getMessage(), e); } } @@ -64,7 +63,7 @@ public class ClassUtils { for (String pkg : packages) { try { return classForName(pkg + "." + className); - } catch (ClassNotFoundException e2) { + } catch (ClassNotFoundException ignore) { } } } @@ -123,7 +122,7 @@ public class ClassUtils { if (className.indexOf('.') == -1) { try { return arrayForName("java.lang." + className); - } catch (ClassNotFoundException e2) { + } catch (ClassNotFoundException ignore) { // ignore, let the original exception be thrown } } @@ -431,7 +430,7 @@ public class ClassUtils { } return map; } - + /** * get simple class name from qualified class name */ @@ -439,7 +438,6 @@ public class ClassUtils { if (null == qualifiedName) { return null; } - int i = qualifiedName.lastIndexOf('.'); return i < 0 ? qualifiedName : qualifiedName.substring(i + 1); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/CtClassBuilder.java b/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/CtClassBuilder.java index 0dc8f88138..7e01df30be 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/CtClassBuilder.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/CtClassBuilder.java @@ -16,11 +16,6 @@ */ package org.apache.dubbo.common.compiler.support; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; @@ -30,6 +25,11 @@ import javassist.CtNewMethod; import javassist.LoaderClassPath; import javassist.NotFoundException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * CtClassBuilder is builder for CtClass *

@@ -43,17 +43,17 @@ public class CtClassBuilder { private String superClassName = "java.lang.Object"; - private List imports = new ArrayList<>(); + private final List imports = new ArrayList<>(); - private Map fullNames = new HashMap<>(); + private final Map fullNames = new HashMap<>(); - private List ifaces = new ArrayList<>(); + private final List ifaces = new ArrayList<>(); - private List constructors = new ArrayList<>(); + private final List constructors = new ArrayList<>(); - private List fields = new ArrayList<>(); + private final List fields = new ArrayList<>(); - private List methods = new ArrayList<>(); + private final List methods = new ArrayList<>(); public String getClassName() { return className; @@ -146,7 +146,7 @@ public class CtClassBuilder { CtClass ctClass = pool.makeClass(className, pool.get(superClassName)); // add imported packages - imports.stream().forEach(pool::importPackage); + imports.forEach(pool::importPackage); // add implemented interfaces for (String iface : ifaces) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/JdkCompiler.java b/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/JdkCompiler.java index 59a764fd43..c82e48dd4c 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/JdkCompiler.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/JdkCompiler.java @@ -61,14 +61,22 @@ public class JdkCompiler extends AbstractCompiler { private final JavaFileManagerImpl javaFileManager; - private volatile List options; + private final List options; - public JdkCompiler() { - options = new ArrayList(); - options.add("-source"); - options.add("1.6"); - options.add("-target"); - options.add("1.6"); + private static final String DEFAULT_JAVA_VERSION = "1.8"; + + private static List buildDefaultOptions(String javaVersion) { + return Arrays.asList( + "-source", javaVersion, "-target", javaVersion + ); + } + + private static List buildDefaultOptions() { + return buildDefaultOptions(DEFAULT_JAVA_VERSION); + } + + public JdkCompiler(List options) { + this.options = new ArrayList<>(options); StandardJavaFileManager manager = compiler.getStandardFileManager(diagnosticCollector, null, null); final ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader instanceof URLClassLoader @@ -93,6 +101,14 @@ public class JdkCompiler extends AbstractCompiler { javaFileManager = new JavaFileManagerImpl(manager, classLoader); } + public JdkCompiler() { + this(buildDefaultOptions()); + } + + public JdkCompiler(String javaVersion) { + this(buildDefaultOptions(javaVersion)); + } + @Override public Class doCompile(String name, String sourceCode) throws Throwable { int i = name.lastIndexOf('.'); @@ -102,7 +118,7 @@ public class JdkCompiler extends AbstractCompiler { javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName, className + ClassUtils.JAVA_EXTENSION, javaFileObject); Boolean result = compiler.getTask(null, javaFileManager, diagnosticCollector, options, - null, Arrays.asList(javaFileObject)).call(); + null, Collections.singletonList(javaFileObject)).call(); if (result == null || !result) { throw new IllegalStateException("Compilation failed. class: " + name + ", diagnostics: " + diagnosticCollector); } @@ -234,7 +250,7 @@ public class JdkCompiler extends AbstractCompiler { } } - private final class ClassLoaderImpl extends ClassLoader { + private static final class ClassLoaderImpl extends ClassLoader { private final Map classes = new HashMap(); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfiguration.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfiguration.java index 31c3684fa3..0cc6af29d8 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfiguration.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfiguration.java @@ -130,7 +130,7 @@ public abstract class AbstractDynamicConfiguration implements DynamicConfigurati @Override public boolean removeConfig(String key, String group) { - return execute(() -> doRemoveConfig(key, group), -1L); + return Boolean.TRUE.equals(execute(() -> doRemoveConfig(key, group), -1L)); } /** diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEvent.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEvent.java index 8195558cd8..f7c2ec71ba 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEvent.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEvent.java @@ -74,8 +74,12 @@ public class ConfigChangedEvent extends EventObject { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ConfigChangedEvent)) return false; + if (this == o) { + return true; + } + if (!(o instanceof ConfigChangedEvent)) { + return false; + } ConfigChangedEvent that = (ConfigChangedEvent) o; return Objects.equals(getKey(), that.getKey()) && Objects.equals(getGroup(), that.getGroup()) && diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/Constants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/Constants.java index d85f86b11c..c0a5d9e408 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/Constants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/Constants.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.common.config.configcenter; +@Deprecated public interface Constants { String CONFIG_CLUSTER_KEY = "config.cluster"; String CONFIG_NAMESPACE_KEY = "config.namespace"; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/TreePathDynamicConfiguration.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/TreePathDynamicConfiguration.java index 6ac9dd0d14..09296b6700 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/TreePathDynamicConfiguration.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/TreePathDynamicConfiguration.java @@ -26,7 +26,7 @@ import java.util.TreeSet; import static java.util.Collections.emptySortedSet; import static java.util.Collections.unmodifiableSortedSet; -import static org.apache.dubbo.common.config.configcenter.Constants.CONFIG_NAMESPACE_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.CONFIG_NAMESPACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR; import static org.apache.dubbo.common.utils.CollectionUtils.isEmpty; import static org.apache.dubbo.common.utils.PathUtils.buildPath; @@ -87,7 +87,7 @@ public abstract class TreePathDynamicConfiguration extends AbstractDynamicConfig @Override public final boolean publishConfig(String key, String group, String content) { String pathKey = buildPathKey(group, key); - return execute(() -> doPublishConfig(pathKey, content), getDefaultTimeout()); + return Boolean.TRUE.equals(execute(() -> doPublishConfig(pathKey, content), getDefaultTimeout())); } @Override diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/ClusterRules.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/ClusterRules.java new file mode 100644 index 0000000000..f5f87299c4 --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/ClusterRules.java @@ -0,0 +1,63 @@ +/* + * 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.constants; + +/** + * constant for Cluster fault-tolerant mode + */ +public interface ClusterRules { + + /** + * When invoke fails, log the initial error and retry other invokers + * (retry n times, which means at most n different invokers will be invoked) + **/ + String FAIL_OVER = "failover"; + + /** + * Execute exactly once, which means this policy will throw an exception immediately in case of an invocation error. + **/ + String FAIL_FAST = "failfast"; + + /** + * When invoke fails, log the error message and ignore this error by returning an empty Result. + **/ + String FAIL_SAFE = "failsafe"; + + /** + * When fails, record failure requests and schedule for retry on a regular interval. + **/ + String FAIL_BACK = "failback"; + + /** + * Invoke a specific number of invokers concurrently, usually used for demanding real-time operations, but need to waste more service resources. + **/ + String FORKING = "forking"; + + /** + * Call all providers by broadcast, call them one by one, and report an error if any one reports an error + **/ + String BROADCAST = "broadcast"; + + + String AVAILABLE = "available"; + + String MERGEABLE = "mergeable"; + + String EMPTY = ""; + + +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java index 9119f323e5..245dd1d55c 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java @@ -114,6 +114,8 @@ public interface CommonConstants { String IO_THREADS_KEY = "iothreads"; + String KEEP_ALIVE_KEY = "keep.alive"; + int DEFAULT_QUEUES = 0; int DEFAULT_ALIVE = 60 * 1000; @@ -207,6 +209,8 @@ public interface CommonConstants { String REMOTE_METADATA_STORAGE_TYPE = "remote"; + String GENERIC_KEY = "generic"; + /** * The composite metadata storage type includes {@link #DEFAULT_METADATA_STORAGE_TYPE "local"} and * {@link #REMOTE_METADATA_STORAGE_TYPE "remote"}. @@ -248,6 +252,11 @@ public interface CommonConstants { String PORT_KEY = "port"; String DUBBO_IP_TO_BIND = "DUBBO_IP_TO_BIND"; + /** + * broadcast cluster. + */ + String BROADCAST_CLUSTER = "broadcast"; + /** * The property name for {@link NetworkInterface#getDisplayName() the name of network interface} that * the Dubbo application prefers @@ -311,6 +320,8 @@ public interface CommonConstants { String GENERIC_SERIALIZATION_NATIVE_JAVA = "nativejava"; + String GENERIC_SERIALIZATION_GSON = "gson"; + String GENERIC_SERIALIZATION_DEFAULT = "true"; String GENERIC_SERIALIZATION_BEAN = "bean"; @@ -401,4 +412,9 @@ public interface CommonConstants { String CLUSTER_INTERCEPTOR_COMPATIBLE_KEY = "dubbo.application.cluster.interceptor.compatible"; String UTF8ENCODE = "UTF-8"; + + /** Pseudo URL prefix for loading from the class path: "classpath:". */ + String CLASSPATH_URL_PREFIX = "classpath:"; + + String DEFAULT_VERSION = "0.0.0"; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoadbalanceRules.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoadbalanceRules.java new file mode 100644 index 0000000000..c0e9285d0e --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoadbalanceRules.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.common.constants; + +/** + * constant for Loadbalance strategy + */ +public interface LoadbalanceRules { + + /** + * This class select one provider from multiple providers randomly. + **/ + String RANDOM = "random"; + + /** + * Round robin load balance. + **/ + String ROUND_ROBIN = "roundrobin"; + + /** + * Filter the number of invokers with the least number of active calls and count the weights and quantities of these invokers. + **/ + String LEAST_ACTIVE = "leastactive"; + + /** + * Consistent Hash, requests with the same parameters are always sent to the same provider. + **/ + String CONSISTENT_HASH = "consistenthash"; + + /** + * Filter the number of invokers with the shortest response time of success calls and count the weights and quantities of these invokers. + **/ + String SHORTEST_RESPONSE = "shortestresponse"; + + String EMPTY = ""; + +} 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 629eaef77b..a7bf66fb66 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 @@ -114,6 +114,11 @@ public class ExtensionLoader { private static volatile LoadingStrategy[] strategies = loadLoadingStrategies(); + /** + * Record all unacceptable exceptions when using SPI + */ + private Set unacceptableExceptions = new ConcurrentHashSet<>(); + public static void setLoadingStrategies(LoadingStrategy... strategies) { if (ArrayUtils.isNotEmpty(strategies)) { ExtensionLoader.strategies = strategies; @@ -624,26 +629,25 @@ public class ExtensionLoader { } private IllegalStateException findException(String name) { - for (Map.Entry entry : exceptions.entrySet()) { - if (entry.getKey().toLowerCase().contains(name.toLowerCase())) { - return entry.getValue(); - } - } StringBuilder buf = new StringBuilder("No such extension " + type.getName() + " by name " + name); - int i = 1; for (Map.Entry entry : exceptions.entrySet()) { - if (i == 1) { - buf.append(", possible causes: "); + if (entry.getKey().toLowerCase().startsWith(name.toLowerCase())) { + if (i == 1) { + buf.append(", possible causes: "); + } + buf.append("\r\n("); + buf.append(i++); + buf.append(") "); + buf.append(entry.getKey()); + buf.append(":\r\n"); + buf.append(StringUtils.toString(entry.getValue())); } + } - buf.append("\r\n("); - buf.append(i++); - buf.append(") "); - buf.append(entry.getKey()); - buf.append(":\r\n"); - buf.append(StringUtils.toString(entry.getValue())); + if (i == 1) { + buf.append(", no related exception was found, please check whether related SPI module is missing."); } return new IllegalStateException(buf.toString()); } @@ -651,13 +655,13 @@ public class ExtensionLoader { @SuppressWarnings("unchecked") private T createExtension(String name, boolean wrap) { Class clazz = getExtensionClasses().get(name); - if (clazz == null) { + if (clazz == null || unacceptableExceptions.contains(name)) { throw findException(name); } try { T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { - EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); + EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } injectExtension(instance); @@ -872,6 +876,7 @@ public class ExtensionLoader { try { try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) { String line; + String clazz = null; while ((line = reader.readLine()) != null) { final int ci = line.indexOf('#'); if (ci >= 0) { @@ -884,10 +889,12 @@ public class ExtensionLoader { int i = line.indexOf('='); if (i > 0) { name = line.substring(0, i).trim(); - line = line.substring(i + 1).trim(); + clazz = line.substring(i + 1).trim(); + } else { + clazz = line; } - if (line.length() > 0 && !isExcluded(line, excludedPackages)) { - loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name, overridden); + if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages)) { + loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden); } } catch (Throwable t) { IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t); @@ -961,6 +968,8 @@ public class ExtensionLoader { if (c == null || overridden) { extensionClasses.put(name, clazz); } else if (c != clazz) { + // duplicate implementation is unacceptable + unacceptableExceptions.add(name); String duplicateMsg = "Duplicate extension " + type.getName() + " name " + name + " on " + c.getName() + " and " + clazz.getName(); logger.error(duplicateMsg); throw new IllegalStateException(duplicateMsg); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Wrapper.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Wrapper.java index 670aae6de0..2d5780d8f1 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Wrapper.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Wrapper.java @@ -16,9 +16,13 @@ */ package org.apache.dubbo.common.extension; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * The annotated class will only work as a wrapper when the condition matches. */ +@Retention(RetentionPolicy.RUNTIME) public @interface Wrapper { /** diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/json/J2oVisitor.java b/dubbo-common/src/main/java/org/apache/dubbo/common/json/J2oVisitor.java index 3aa3fd9ed1..bc6e4e39bb 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/json/J2oVisitor.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/json/J2oVisitor.java @@ -259,9 +259,7 @@ class J2oVisitor implements JSONVisitor { try { mValue = mType.newInstance(); mWrapper = Wrapper.getWrapper(mType); - } catch (IllegalAccessException e) { - throw new ParseException(StringUtils.toString(e)); - } catch (InstantiationException e) { + } catch (IllegalAccessException | InstantiationException e) { throw new ParseException(StringUtils.toString(e)); } } @@ -304,9 +302,7 @@ class J2oVisitor implements JSONVisitor { field.setAccessible(true); } field.set(mValue, obj); - } catch (NoSuchFieldException e) { - throw new ParseException(StringUtils.toString(e)); - } catch (IllegalAccessException e) { + } catch (NoSuchFieldException | IllegalAccessException e) { throw new ParseException(StringUtils.toString(e)); } } else if (!CLASS_PROPERTY.equals(name)) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/lang/Prioritized.java b/dubbo-common/src/main/java/org/apache/dubbo/common/lang/Prioritized.java index babb3ed3c9..76a6a0b8af 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/lang/Prioritized.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/lang/Prioritized.java @@ -63,7 +63,7 @@ public interface Prioritized extends Comparable { /** * Get the priority * - * @return the default is {@link #MIN_PRIORITY minimum one} + * @return the default is {@link #NORMAL_PRIORITY} */ default int getPriority() { return NORMAL_PRIORITY; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalRunnable.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalRunnable.java new file mode 100644 index 0000000000..6cc8db8fee --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalRunnable.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.common.threadlocal; + + +/** + * InternalRunnable + * There is a risk of memory leak when using {@link InternalThreadLocal} without calling + * {@link InternalThreadLocal#removeAll()}. + * This design is learning from {@see io.netty.util.concurrent.FastThreadLocalRunnable} which is in Netty. + */ +public class InternalRunnable implements Runnable{ + private final Runnable runnable; + + public InternalRunnable(Runnable runnable){ + this.runnable=runnable; + } + + /** + * After the task execution is completed, it will call {@link InternalThreadLocal#removeAll()} to clear + * unnecessary variables in the thread. + */ + @Override + public void run() { + try{ + runnable.run(); + }finally { + InternalThreadLocal.removeAll(); + } + } + + /** + * Wrap ordinary Runnable into {@link InternalThreadLocal}. + */ + static Runnable Wrap(Runnable runnable){ + return runnable instanceof InternalRunnable?runnable:new InternalRunnable(runnable); + } +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThreadLocal.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThreadLocal.java index 8820f12cc2..13c7463db8 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThreadLocal.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThreadLocal.java @@ -58,7 +58,7 @@ public class InternalThreadLocal { if (v != null && v != InternalThreadLocalMap.UNSET) { Set> variablesToRemove = (Set>) v; InternalThreadLocal[] variablesToRemoveArray = - variablesToRemove.toArray(new InternalThreadLocal[variablesToRemove.size()]); + variablesToRemove.toArray(new InternalThreadLocal[0]); for (InternalThreadLocal tlv : variablesToRemoveArray) { tlv.remove(threadLocalMap); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/NamedInternalThreadFactory.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/NamedInternalThreadFactory.java index 52b8d562c0..0bb305f03f 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/NamedInternalThreadFactory.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/NamedInternalThreadFactory.java @@ -40,7 +40,7 @@ public class NamedInternalThreadFactory extends NamedThreadFactory { @Override public Thread newThread(Runnable runnable) { String name = mPrefix + mThreadNum.getAndIncrement(); - InternalThread ret = new InternalThread(mGroup, runnable, name, 0); + InternalThread ret = new InternalThread(mGroup, InternalRunnable.Wrap(runnable), name, 0); ret.setDaemon(mDaemon); return ret; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java index 0225132e72..bc9042c8e4 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java @@ -32,7 +32,7 @@ import java.util.concurrent.TimeoutException; /** * The most important difference between this Executor and other normal Executor is that this one doesn't manage * any thread. - * + *

* Tasks submitted to this executor through {@link #execute(Runnable)} will not get scheduled to a specific thread, though normal executors always do the schedule. * Those tasks are stored in a blocking queue and will only be executed when a thread calls {@link #waitAndDrain()}, the thread executing the task * is exactly the same as the one calling waitAndDrain. @@ -86,7 +86,13 @@ public class ThreadlessExecutor extends AbstractExecutorService { return; } - Runnable runnable = queue.take(); + Runnable runnable; + try { + runnable = queue.take(); + }catch (InterruptedException e){ + waiting = false; + throw e; + } synchronized (lock) { waiting = false; @@ -95,12 +101,7 @@ public class ThreadlessExecutor extends AbstractExecutorService { runnable = queue.poll(); while (runnable != null) { - try { - runnable.run(); - } catch (Throwable t) { - logger.info(t); - - } + runnable.run(); runnable = queue.poll(); } // mark the status of ThreadlessExecutor as finished. @@ -131,6 +132,7 @@ public class ThreadlessExecutor extends AbstractExecutorService { */ @Override public void execute(Runnable runnable) { + runnable = new RunnableWrapper(runnable); synchronized (lock) { if (!waiting) { sharedExecutor.execute(runnable); @@ -180,4 +182,21 @@ public class ThreadlessExecutor extends AbstractExecutorService { public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return false; } + + private static class RunnableWrapper implements Runnable { + private Runnable runnable; + + public RunnableWrapper(Runnable runnable) { + this.runnable = runnable; + } + + @Override + public void run() { + try { + runnable.run(); + } catch (Throwable t) { + logger.info(t); + } + } + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReport.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReport.java index c6865eb225..49fdd89379 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReport.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReport.java @@ -16,6 +16,14 @@ */ package org.apache.dubbo.common.threadpool.support; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedEvent; +import org.apache.dubbo.common.utils.JVMUtil; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.event.EventDispatcher; + import java.io.File; import java.io.FileOutputStream; import java.text.SimpleDateFormat; @@ -26,13 +34,7 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadPoolExecutor; -import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedEvent; -import org.apache.dubbo.common.utils.JVMUtil; -import org.apache.dubbo.event.EventDispatcher; - +import static java.lang.String.format; import static org.apache.dubbo.common.constants.CommonConstants.DUMP_DIRECTORY; /** @@ -61,6 +63,8 @@ public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy { private static Semaphore guard = new Semaphore(1); + private static final String USER_HOME = System.getProperty("user.home"); + public AbortPolicyWithReport(String threadName, URL url) { this.threadName = threadName; this.url = url; @@ -104,7 +108,7 @@ public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy { ExecutorService pool = Executors.newSingleThreadExecutor(); pool.execute(() -> { - String dumpPath = url.getParameter(DUMP_DIRECTORY, System.getProperty("user.home")); + String dumpPath = getDumpPath(); SimpleDateFormat sdf; @@ -134,4 +138,21 @@ public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy { } + private String getDumpPath() { + final String dumpPath = url.getParameter(DUMP_DIRECTORY); + if (StringUtils.isEmpty(dumpPath)) { + return USER_HOME; + } + final File dumpDirectory = new File(dumpPath); + if (!dumpDirectory.exists()) { + if (dumpDirectory.mkdirs()) { + logger.info(format("Dubbo dump directory[%s] created", dumpDirectory.getAbsolutePath())); + } else { + logger.warn(format("Dubbo dump directory[%s] can't be created, use the 'user.home'[%s]", + dumpDirectory.getAbsolutePath(), USER_HOME)); + return USER_HOME; + } + } + return dumpPath; + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java b/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java index d695b46d16..487cc74ba5 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java @@ -805,8 +805,10 @@ public class HashedWheelTimer implements Timer { return head; } } - + + private static final boolean IS_OS_WINDOWS = System.getProperty("os.name", "").toLowerCase(Locale.US).contains("win"); + private boolean isWindows() { - return System.getProperty("os.name", "").toLowerCase(Locale.US).contains("win"); + return IS_OS_WINDOWS; } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java index 4c333eabe6..6110639376 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java @@ -37,6 +37,7 @@ import java.util.StringJoiner; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX; import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; /** * A class which store parameters for {@link URL} @@ -178,7 +179,7 @@ public class URLParam implements Serializable { * Specially, in some situation like `method1.1.callback=true`, key is `1.callback`. * * @param method method name - * @param key key + * @param key key * @return value */ public String getMethodParameter(String method, String key) { @@ -191,7 +192,7 @@ public class URLParam implements Serializable { * Specially, in some situation like `method1.1.callback=true`, key is `1.callback`. * * @param method method name - * @param key key + * @param key key * @return value */ public String getMethodParameterStrict(String method, String key) { @@ -853,10 +854,27 @@ public class URLParam implements Serializable { return false; } URLParam urlParam = (URLParam) o; - return Objects.equals(KEY, urlParam.KEY) + + if (Objects.equals(KEY, urlParam.KEY) && Objects.equals(DEFAULT_KEY, urlParam.DEFAULT_KEY) - && Arrays.equals(VALUE, urlParam.VALUE) - && Objects.equals(EXTRA_PARAMS, urlParam.EXTRA_PARAMS); + && Arrays.equals(VALUE, urlParam.VALUE)) { + if (CollectionUtils.isNotEmptyMap(EXTRA_PARAMS)) { + if (CollectionUtils.isEmptyMap(urlParam.EXTRA_PARAMS) || EXTRA_PARAMS.size() != urlParam.EXTRA_PARAMS.size()) { + return false; + } + for (Map.Entry entry : EXTRA_PARAMS.entrySet()) { + if (TIMESTAMP_KEY.equals(entry.getKey())) { + continue; + } + if (!entry.getValue().equals(urlParam.EXTRA_PARAMS.get(entry.getKey()))) { + return false; + } + } + return true; + } + return CollectionUtils.isEmptyMap(urlParam.EXTRA_PARAMS); + } + return false; } private int hashCodeCache = -1; @@ -864,7 +882,11 @@ public class URLParam implements Serializable { @Override public int hashCode() { if (hashCodeCache == -1) { - hashCodeCache = EXTRA_PARAMS.hashCode(); + for (Map.Entry entry : EXTRA_PARAMS.entrySet()) { + if (!TIMESTAMP_KEY.equals(entry.getKey())) { + hashCodeCache = hashCodeCache * 31 + Objects.hashCode(entry); + } + } for (Integer value : VALUE) { hashCodeCache = hashCodeCache * 31 + value; } 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 index 600c8aaef9..8da5f1d05d 100644 --- 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 @@ -22,6 +22,7 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; @@ -40,6 +41,7 @@ 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.findMethod; import static org.apache.dubbo.common.utils.MethodUtils.invokeMethod; /** @@ -449,4 +451,32 @@ public interface AnnotationUtils { Class... annotationTypes) { return isAnnotationPresent(type, false, annotationTypes); } + + + /** + * Get the default value of attribute on the specified annotation + * + * @param annotation {@link Annotation} object + * @param attributeName the name of attribute + * @param the type of value + * @return null if not found + * @since 2.7.9 + */ + static T getDefaultValue(Annotation annotation, String attributeName) { + return getDefaultValue(annotation.annotationType(), attributeName); + } + + /** + * Get the default value of attribute on the specified annotation + * + * @param annotationType the type of {@link Annotation} + * @param attributeName the name of attribute + * @param the type of value + * @return null if not found + * @since 2.7.9 + */ + static T getDefaultValue(Class annotationType, String attributeName) { + Method method = findMethod(annotationType, attributeName); + return (T) (method == null ? null : method.getDefaultValue()); + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ArrayUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ArrayUtils.java index 5be000d78f..e648598867 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ArrayUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ArrayUtils.java @@ -66,4 +66,16 @@ public final class ArrayUtils { return -1; } } + + /** + * Convert from variable arguments to array + * + * @param values variable arguments + * @param The class + * @return array + * @since 2.7.9 + */ + public static T[] of(T... values) { + return values; + } } 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 2fb515c9fc..a437a7d6a7 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 @@ -43,21 +43,6 @@ public class ClassUtils { * Suffix for array class names: "[]" */ public static final String ARRAY_SUFFIX = "[]"; - /** - * Prefix for internal array class names: "[L" - */ - private static final String INTERNAL_ARRAY_PREFIX = "[L"; - /** - * Map with primitive type name as key and corresponding primitive type as - * value, for example: "int" -> "int.class". - */ - private static final Map> PRIMITIVE_TYPE_NAME_MAP = new HashMap>(32); - /** - * Map with primitive wrapper type as key and corresponding primitive type - * as value, for example: Integer.class -> int.class. - */ - private static final Map, Class> PRIMITIVE_WRAPPER_TYPE_MAP = new HashMap, Class>(16); - /** * Simple Types including: *

    @@ -94,7 +79,20 @@ public class ClassUtils { Date.class, Object.class ); - + /** + * Prefix for internal array class names: "[L" + */ + private static final String INTERNAL_ARRAY_PREFIX = "[L"; + /** + * Map with primitive type name as key and corresponding primitive type as + * value, for example: "int" -> "int.class". + */ + private static final Map> PRIMITIVE_TYPE_NAME_MAP = new HashMap>(32); + /** + * Map with primitive wrapper type as key and corresponding primitive type + * as value, for example: Integer.class -> int.class. + */ + private static final Map, Class> PRIMITIVE_WRAPPER_TYPE_MAP = new HashMap, Class>(16); private static final char PACKAGE_SEPARATOR_CHAR = '.'; static { 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 1b73370c0b..13c55eb133 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 @@ -39,28 +39,25 @@ import static java.util.Collections.unmodifiableSet; */ public class CollectionUtils { - private static final Comparator SIMPLE_NAME_COMPARATOR = new Comparator() { - @Override - public int compare(String s1, String s2) { - if (s1 == null && s2 == null) { - return 0; - } - if (s1 == null) { - return -1; - } - if (s2 == null) { - return 1; - } - int i1 = s1.lastIndexOf('.'); - if (i1 >= 0) { - s1 = s1.substring(i1 + 1); - } - int i2 = s2.lastIndexOf('.'); - if (i2 >= 0) { - s2 = s2.substring(i2 + 1); - } - return s1.compareToIgnoreCase(s2); + private static final Comparator SIMPLE_NAME_COMPARATOR = (s1, s2) -> { + if (s1 == null && s2 == null) { + return 0; } + if (s1 == null) { + return -1; + } + if (s2 == null) { + return 1; + } + int i1 = s1.lastIndexOf('.'); + if (i1 >= 0) { + s1 = s1.substring(i1 + 1); + } + int i2 = s2.lastIndexOf('.'); + if (i2 >= 0) { + s2 = s2.substring(i2 + 1); + } + return s1.compareToIgnoreCase(s2); }; private CollectionUtils() { @@ -324,9 +321,7 @@ public class CollectionUtils { try { return one.containsAll(another); - } catch (ClassCastException unused) { - return false; - } catch (NullPointerException unused) { + } catch (ClassCastException | NullPointerException unused) { return false; } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CompatibleTypeUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CompatibleTypeUtils.java index 5d5cc5c80a..fecec8b992 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CompatibleTypeUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CompatibleTypeUtils.java @@ -21,6 +21,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collection; @@ -111,17 +112,23 @@ public class CompatibleTypeUtils { + DATE_FORMAT + ", cause: " + e.getMessage(), e); } } - if (type == java.time.LocalDateTime.class || type == java.time.LocalDate.class - || type == java.time.LocalTime.class) { - - LocalDateTime localDateTime = LocalDateTime.parse(string); - if (type == java.time.LocalDate.class) { - return localDateTime.toLocalDate(); + if (type == java.time.LocalDateTime.class) { + if (StringUtils.isEmpty(string)) { + return null; } - if (type == java.time.LocalTime.class) { - return localDateTime.toLocalTime(); + return LocalDateTime.parse(string); + } + if (type == java.time.LocalDate.class) { + if (StringUtils.isEmpty(string)) { + return null; } - return localDateTime; + return LocalDate.parse(string); + } + if (type == java.time.LocalTime.class) { + if (StringUtils.isEmpty(string)) { + return null; + } + return LocalDateTime.parse(string).toLocalTime(); } if (type == Class.class) { try { @@ -200,19 +207,19 @@ public class CompatibleTypeUtils { } } if (value.getClass().isArray() && Collection.class.isAssignableFrom(type)) { + int length = Array.getLength(value); Collection collection; if (!type.isInterface()) { try { collection = (Collection) type.newInstance(); } catch (Throwable e) { - collection = new ArrayList(); + collection = new ArrayList(length); } } else if (type == Set.class) { - collection = new HashSet(); + collection = new HashSet(Math.max((int) (length/.75f) + 1, 16)); } else { - collection = new ArrayList(); + collection = new ArrayList(length); } - int length = Array.getLength(value); for (int i = 0; i < length; i++) { collection.add(Array.get(value, i)); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ExecutorUtil.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ExecutorUtil.java index 93a2a60d7d..97f13a5f96 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ExecutorUtil.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ExecutorUtil.java @@ -60,9 +60,7 @@ public class ExecutorUtil { try { // Disable new tasks from being submitted es.shutdown(); - } catch (SecurityException ex2) { - return; - } catch (NullPointerException ex2) { + } catch (SecurityException | NullPointerException ex2) { return; } try { @@ -86,9 +84,7 @@ public class ExecutorUtil { final ExecutorService es = (ExecutorService) executor; try { es.shutdownNow(); - } catch (SecurityException ex2) { - return; - } catch (NullPointerException ex2) { + } catch (SecurityException | NullPointerException ex2) { return; } try { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/FieldUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/FieldUtils.java index a6192390a9..5c24ce11a2 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/FieldUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/FieldUtils.java @@ -62,6 +62,11 @@ public interface FieldUtils { break; } } + + if (field == null) { + throw new IllegalStateException(String.format("cannot find field %s,field is null", fieldName)); + } + return field; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/IOUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/IOUtils.java index 02ec00bdca..b7e26169cc 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/IOUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/IOUtils.java @@ -16,9 +16,12 @@ */ package org.apache.dubbo.common.utils; +import org.apache.dubbo.common.constants.CommonConstants; + import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -30,6 +33,8 @@ import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; import java.util.List; @@ -228,4 +233,38 @@ public class IOUtils { writeLines(new FileOutputStream(file, true), lines); } + + /** + * use like spring code + * @param resourceLocation + * @return + */ + public static URL getURL(String resourceLocation) throws FileNotFoundException { + Assert.notNull(resourceLocation, "Resource location must not be null"); + if (resourceLocation.startsWith(CommonConstants.CLASSPATH_URL_PREFIX)) { + String path = resourceLocation.substring(CommonConstants.CLASSPATH_URL_PREFIX.length()); + ClassLoader cl = ClassUtils.getClassLoader(); + URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path)); + if (url == null) { + String description = "class path resource [" + path + "]"; + throw new FileNotFoundException(description + + " cannot be resolved to URL because it does not exist"); + } + return url; + } + try { + // try URL + return new URL(resourceLocation); + } + catch (MalformedURLException ex) { + // no URL -> treat as file path + try { + return new File(resourceLocation).toURI().toURL(); + } + catch (MalformedURLException ex2) { + throw new FileNotFoundException("Resource location [" + resourceLocation + + "] is neither a URL not a well-formed file path"); + } + } + } } \ No newline at end of file diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LFUCache.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LFUCache.java index a4c373239f..8230bd6886 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LFUCache.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LFUCache.java @@ -30,12 +30,12 @@ public class LFUCache { private int curSize = 0; private final ReentrantLock lock = new ReentrantLock(); - private static final int DEFAULT_LOAD_FACTOR = 1000; + private static final int DEFAULT_INITIAL_CAPACITY = 1000; - private static final float DEFAULT_EVICTION_CAPACITY = 0.75f; + private static final float DEFAULT_EVICTION_FACTOR = 0.75f; public LFUCache() { - this(DEFAULT_LOAD_FACTOR, DEFAULT_EVICTION_CAPACITY); + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_EVICTION_FACTOR); } /** @@ -46,12 +46,13 @@ public class LFUCache { * @param maxCapacity cache max capacity * @param evictionFactor cache proceedEviction factor */ + @SuppressWarnings("unchecked") public LFUCache(final int maxCapacity, final float evictionFactor) { if (maxCapacity <= 0) { throw new IllegalArgumentException("Illegal initial capacity: " + maxCapacity); } - boolean factorInRange = evictionFactor <= 1 || evictionFactor < 0; + boolean factorInRange = evictionFactor <= 1 && evictionFactor > 0; if (!factorInRange || Float.isNaN(evictionFactor)) { throw new IllegalArgumentException("Illegal eviction factor value:" + evictionFactor); @@ -61,7 +62,7 @@ public class LFUCache { this.map = new HashMap<>(); this.freqTable = new CacheDeque[capacity + 1]; for (int i = 0; i <= capacity; i++) { - freqTable[i] = new CacheDeque(); + freqTable[i] = new CacheDeque<>(); } for (int i = 0; i < capacity; i++) { freqTable[i].nextDeque = freqTable[i + 1]; @@ -77,11 +78,9 @@ public class LFUCache { CacheNode node; lock.lock(); try { - if (map.containsKey(key)) { - node = map.get(key); - if (node != null) { - CacheNode.withdrawNode(node); - } + node = map.get(key); + if (node != null) { + CacheNode.withdrawNode(node); node.value = value; freqTable[0].addLastNode(node); map.put(key, node); @@ -171,7 +170,7 @@ public class LFUCache { CacheNode next; K key; V value; - CacheDeque owner; + CacheDeque owner; CacheNode() { } 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 27262b48ea..a885fb7c27 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 @@ -34,6 +34,7 @@ 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; +import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; /** * Miscellaneous method utility methods. @@ -50,7 +51,7 @@ public interface MethodUtils { * @param method the method to check * @return whether the given method is setter method */ - public static boolean isSetter(Method method) { + static boolean isSetter(Method method) { return method.getName().startsWith("set") && !"set".equals(method.getName()) && Modifier.isPublic(method.getModifiers()) @@ -65,7 +66,7 @@ public interface MethodUtils { * @param method the method to check * @return whether the given method is getter method */ - public static boolean isGetter(Method method) { + static boolean isGetter(Method method) { String name = method.getName(); return (name.startsWith("get") || name.startsWith("is")) && !"get".equals(name) && !"is".equals(name) @@ -82,7 +83,7 @@ public interface MethodUtils { * @param method the method to check * @return whether the given method is meta method */ - public static boolean isMetaMethod(Method method) { + static boolean isMetaMethod(Method method) { String name = method.getName(); if (!(name.startsWith("get") || name.startsWith("is"))) { return false; @@ -113,12 +114,11 @@ public interface MethodUtils { * @param method the method to check * @return whether the given method is deprecated method */ - public static boolean isDeprecated(Method method) { + 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 * @@ -252,7 +252,9 @@ public interface MethodUtils { static Method findMethod(Class type, String methodName, Class... parameterTypes) { Method method = null; try { - method = type.getDeclaredMethod(methodName, parameterTypes); + if (type != null && isNotEmpty(methodName)) { + method = type.getDeclaredMethod(methodName, parameterTypes); + } } catch (NoSuchMethodException e) { } return method; @@ -274,6 +276,10 @@ public interface MethodUtils { Method method = findMethod(type, methodName, parameterTypes); T value = null; + if (method == null) { + throw new IllegalStateException(String.format("cannot find method %s,class: %s", methodName, type.getName())); + } + try { final boolean isAccessible = method.isAccessible(); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java index 1845514ba8..01855f4b5f 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java @@ -235,7 +235,9 @@ public class NetUtils { return configIp; } - return getIpByHost(getLocalAddress().getHostName()); + InetAddress localAddress = getLocalAddress(); + String hostName = localAddress == null ? LOCALHOST_VALUE : localAddress.getHostName(); + return getIpByHost(hostName); } /** @@ -471,10 +473,8 @@ public class NetUtils { public static void setInterface(MulticastSocket multicastSocket, boolean preferIpv6) throws IOException { boolean interfaceSet = false; - Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); - while (interfaces.hasMoreElements()) { - NetworkInterface i = (NetworkInterface) interfaces.nextElement(); - Enumeration addresses = i.getInetAddresses(); + for (NetworkInterface networkInterface : getValidNetworkInterfaces()) { + Enumeration addresses = networkInterface.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress address = (InetAddress) addresses.nextElement(); if (preferIpv6 && address instanceof Inet6Address) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java index 145550c4ef..b940d047d8 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java @@ -173,6 +173,7 @@ public class PojoUtils { } for (Method method : pojo.getClass().getMethods()) { if (ReflectUtils.isBeanPropertyReadMethod(method)) { + ReflectUtils.makeAccessible(method); try { map.put(ReflectUtils.getPropertyNameFromBeanReadMethod(method), generalize(method.invoke(pojo), history)); } catch (Exception e) { @@ -588,11 +589,7 @@ public class PojoUtils { constructor.setAccessible(true); Object[] parameters = Arrays.stream(constructor.getParameterTypes()).map(PojoUtils::getDefaultValue).toArray(); return constructor.newInstance(parameters); - } catch (InstantiationException e) { - throw new RuntimeException(e.getMessage(), e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e.getMessage(), e); - } catch (InvocationTargetException e) { + } catch (InstantiationException | InvocationTargetException | IllegalAccessException e) { throw new RuntimeException(e.getMessage(), e); } } @@ -608,9 +605,15 @@ public class PojoUtils { if ("char".equals(parameterType.getName())) { return Character.MIN_VALUE; } - if ("bool".equals(parameterType.getName())) { + if ("boolean".equals(parameterType.getName())) { return false; } + if ("byte".equals(parameterType.getName())) { + return (byte) 0; + } + if ("short".equals(parameterType.getName())) { + return (short) 0; + } return parameterType.isPrimitive() ? 0 : null; } 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 ea8d51f764..c73df9ae96 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 @@ -31,6 +31,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.net.URL; import java.security.CodeSource; import java.security.ProtectionDomain; @@ -154,8 +155,8 @@ public final class ReflectUtils { } public static boolean isPrimitives(Class cls) { - if (cls.isArray()) { - return isPrimitive(cls.getComponentType()); + while (cls.isArray()) { + cls = cls.getComponentType(); } return isPrimitive(cls); } @@ -904,7 +905,9 @@ public final class ReflectUtils { * @throws NoSuchMethodException * @throws ClassNotFoundException * @throws IllegalStateException when multiple methods are found (overridden method when parameter info is not provided) + * @deprecated Recommend {@link MethodUtils#findMethod(Class, String, Class[])} */ + @Deprecated public static Method findMethodByMethodSignature(Class clazz, String methodName, String[] parameterTypes) throws NoSuchMethodException, ClassNotFoundException { String signature = clazz.getName() + "." + methodName; @@ -943,6 +946,16 @@ public final class ReflectUtils { return method; } + /** + * @param clazz Target class to find method + * @param methodName Method signature, e.g.: method1(int, String). It is allowed to provide method name only, e.g.: method2 + * @return target method + * @throws NoSuchMethodException + * @throws ClassNotFoundException + * @throws IllegalStateException when multiple methods are found (overridden method when parameter info is not provided) + * @deprecated Recommend {@link MethodUtils#findMethod(Class, String, Class[])} + */ + @Deprecated public static Method findMethodByMethodName(Class clazz, String methodName) throws NoSuchMethodException, ClassNotFoundException { return findMethodByMethodSignature(clazz, methodName, null); @@ -1051,7 +1064,7 @@ public final class ReflectUtils { try { Object value = emptyInstances.get(returnType); if (value == null) { - value = returnType.newInstance(); + value = returnType.getDeclaredConstructor().newInstance(); emptyInstances.put(returnType, value); } Class cls = value.getClass(); @@ -1190,6 +1203,9 @@ public final class ReflectUtils { if (actualArgType instanceof ParameterizedType) { returnType = (Class) ((ParameterizedType) actualArgType).getRawType(); genericReturnType = actualArgType; + } else if (actualArgType instanceof TypeVariable) { + returnType = (Class) ((TypeVariable) actualArgType).getBounds()[0]; + genericReturnType = actualArgType; } else { returnType = (Class) actualArgType; genericReturnType = returnType; @@ -1217,16 +1233,14 @@ public final class ReflectUtils { Set parameterizedTypes = genericTypes.stream() .filter(type -> type instanceof ParameterizedType)// filter ParameterizedType - .map(type -> ParameterizedType.class.cast(type)) // cast to ParameterizedType + .map(ParameterizedType.class::cast) // cast to ParameterizedType .collect(Collectors.toSet()); if (parameterizedTypes.isEmpty()) { // If not found, try to search super types recursively genericTypes.stream() .filter(type -> type instanceof Class) - .map(type -> Class.class.cast(type)) - .forEach(superClass -> { - parameterizedTypes.addAll(findParameterizedTypes(superClass)); - }); + .map(Class.class::cast) + .forEach(superClass -> parameterizedTypes.addAll(findParameterizedTypes(superClass))); } return unmodifiableSet(parameterizedTypes); // build as a Set @@ -1316,4 +1330,21 @@ public final class ReflectUtils { return types; } -} \ No newline at end of file + + /** + * Copy from org.springframework.util.ReflectionUtils. + * Make the given method accessible, explicitly setting it accessible if + * necessary. The {@code setAccessible(true)} method is only called + * when actually necessary, to avoid unnecessary conflicts with a JVM + * SecurityManager (if active). + * @param method the method to make accessible + * @see java.lang.reflect.Method#setAccessible + */ + @SuppressWarnings("deprecation") // on JDK 9 + public static void makeAccessible(Method method) { + if ((!Modifier.isPublic(method.getModifiers()) || + !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) { + method.setAccessible(true); + } + } +} 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 index 58e5e7a9b4..c49b3a368c 100644 --- 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 @@ -16,11 +16,15 @@ */ package org.apache.dubbo.common.utils; +import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.Service; import java.lang.annotation.Annotation; +import java.util.List; import static java.lang.String.format; +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableList; 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; @@ -36,6 +40,13 @@ import static org.apache.dubbo.common.utils.StringUtils.isEmpty; */ public class ServiceAnnotationResolver { + /** + * The annotation {@link Class classes} of Dubbo Service (read-only) + * + * @since 2.7.9 + */ + public static List> SERVICE_ANNOTATION_CLASSES = unmodifiableList(asList(DubboService.class, Service.class, com.alibaba.dubbo.config.annotation.Service.class)); + private final Annotation serviceAnnotation; private final Class serviceType; @@ -47,16 +58,18 @@ public class ServiceAnnotationResolver { private Annotation getServiceAnnotation(Class serviceType) { - Annotation serviceAnnotation = serviceType.getAnnotation(Service.class); + Annotation serviceAnnotation = null; - if (serviceAnnotation == null) { - serviceAnnotation = serviceType.getAnnotation(com.alibaba.dubbo.config.annotation.Service.class); + for (Class serviceAnnotationClass : SERVICE_ANNOTATION_CLASSES) { + serviceAnnotation = serviceType.getAnnotation(serviceAnnotationClass); + if (serviceAnnotation != null) { + break; + } } 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(), + throw new IllegalArgumentException(format("Any annotation of [%s] can't be annotated in the service type[%s].", + SERVICE_ANNOTATION_CLASSES, serviceType.getName() )); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java index 968dc7e72c..3f21374737 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java @@ -200,11 +200,7 @@ public class UrlUtils { if (version != null && version.length() > 0) { name = name + ":" + version; } - Map newUrls = newRegister.get(name); - if (newUrls == null) { - newUrls = new HashMap(); - newRegister.put(name, newUrls); - } + Map newUrls = newRegister.computeIfAbsent(name, k -> new HashMap<>()); newUrls.put(serviceUrl, StringUtils.toQueryString(params)); } } else { @@ -261,11 +257,7 @@ public class UrlUtils { params.put("version", name.substring(i + 1)); name = name.substring(0, i); } - Map newUrls = newRegister.get(name); - if (newUrls == null) { - newUrls = new HashMap(); - newRegister.put(name, newUrls); - } + Map newUrls = newRegister.computeIfAbsent(name, k -> new HashMap()); newUrls.put(serviceUrl, StringUtils.toQueryString(params)); } } else { @@ -324,11 +316,7 @@ public class UrlUtils { if (version != null && version.length() > 0) { name = name + ":" + version; } - Map newUrls = newNotify.get(name); - if (newUrls == null) { - newUrls = new HashMap(); - newNotify.put(name, newUrls); - } + Map newUrls = newNotify.computeIfAbsent(name, k -> new HashMap()); newUrls.put(url, StringUtils.toQueryString(params)); } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java index 3e0c9f6b3d..97ae6a180c 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java @@ -34,6 +34,7 @@ import org.apache.dubbo.rpc.model.AsyncMethodInfo; import javax.annotation.PostConstruct; import java.io.Serializable; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; @@ -86,6 +87,11 @@ public abstract class AbstractConfig implements Serializable { protected final AtomicBoolean refreshed = new AtomicBoolean(false); + /** + * Is default or not + */ + protected Boolean isDefault; + private static String convertLegacyValue(String key, String value) { if (value != null && value.length() > 0) { if ("dubbo.service.max.retry.providers".equals(key)) { @@ -373,6 +379,7 @@ public abstract class AbstractConfig implements Serializable { Method[] methods = annotationClass.getMethods(); for (Method method : methods) { if (method.getDeclaringClass() != Object.class + && method.getDeclaringClass()!= Annotation.class && method.getReturnType() != void.class && method.getParameterTypes().length == 0 && Modifier.isPublic(method.getModifiers()) @@ -536,7 +543,7 @@ public abstract class AbstractConfig implements Serializable { buf.append(" "); buf.append(key); buf.append("=\""); - buf.append(value); + buf.append(key.equals("password") ? "******" : value); buf.append("\""); } } @@ -628,4 +635,12 @@ public abstract class AbstractConfig implements Serializable { return hashCode; } + + public Boolean isDefault() { + return isDefault; + } + + public void setDefault(Boolean isDefault) { + this.isDefault = isDefault; + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java index e14166822a..bf1497daba 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java @@ -26,6 +26,7 @@ import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ServiceMetadata; import java.util.ArrayList; import java.util.Arrays; @@ -52,6 +53,21 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig { private static final long serialVersionUID = -1559314110797223229L; + /** + * The interface name of the exported service + */ + protected String interfaceName; + /** + * The remote service version the customer/provider side will reference + */ + protected String version; + + /** + * The remote service group the customer/provider side will reference + */ + protected String group; + + protected ServiceMetadata serviceMetadata; /** * Local impl class name for the service interface */ @@ -312,6 +328,10 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig { } + protected boolean notHasSelfRegistryProperty() { + return CollectionUtils.isEmpty(registries) && StringUtils.isEmpty(registryIds); + } + public void completeCompoundConfigs(AbstractInterfaceConfig interfaceConfig) { if (interfaceConfig != null) { if (application == null) { @@ -320,15 +340,16 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig { if (module == null) { setModule(interfaceConfig.getModule()); } - if (registries == null) { + if (notHasSelfRegistryProperty()) { setRegistries(interfaceConfig.getRegistries()); + setRegistryIds(interfaceConfig.getRegistryIds()); } if (monitor == null) { setMonitor(interfaceConfig.getMonitor()); } } if (module != null) { - if (registries == null) { + if (notHasSelfRegistryProperty()) { setRegistries(module.getRegistries()); } if (monitor == null) { @@ -336,8 +357,9 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig { } } if (application != null) { - if (registries == null) { + if (notHasSelfRegistryProperty()) { setRegistries(application.getRegistries()); + setRegistryIds(application.getRegistryIds()); } if (monitor == null) { setMonitor(application.getMonitor()); @@ -346,10 +368,9 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig { } protected void computeValidRegistryIds() { - if (StringUtils.isEmpty(getRegistryIds())) { - if (getApplication() != null && StringUtils.isNotEmpty(getApplication().getRegistryIds())) { - setRegistryIds(getApplication().getRegistryIds()); - } + if (application != null && notHasSelfRegistryProperty()) { + setRegistries(application.getRegistries()); + setRegistryIds(application.getRegistryIds()); } } @@ -691,4 +712,46 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig { public SslConfig getSslConfig() { return ApplicationModel.getConfigManager().getSsl().orElse(null); } + + public void initServiceMetadata(AbstractInterfaceConfig interfaceConfig) { + serviceMetadata.setVersion(getVersion(interfaceConfig)); + serviceMetadata.setGroup(getGroup(interfaceConfig)); + serviceMetadata.setDefaultGroup(getGroup(interfaceConfig)); + serviceMetadata.setServiceInterfaceName(getInterface()); + } + + public String getGroup(AbstractInterfaceConfig interfaceConfig) { + return StringUtils.isEmpty(this.group) ? (interfaceConfig != null ? interfaceConfig.getGroup() : this.group) : this.group; + } + + public String getVersion(AbstractInterfaceConfig interfaceConfig) { + return StringUtils.isEmpty(this.version) ? (interfaceConfig != null ? interfaceConfig.getVersion() : this.version) : this.version; + } + + 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 String getInterface() { + return interfaceName; + } + + public void setInterface(String interfaceName) { + this.interfaceName = interfaceName; +// if (StringUtils.isEmpty(id)) { +// id = interfaceName; +// } + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java index 27d4ddc9a0..f2038a1117 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java @@ -71,15 +71,7 @@ public abstract class AbstractReferenceConfig extends AbstractInterfaceConfig { //TODO solve merge problem protected Boolean stubevent;//= Constants.DEFAULT_STUB_EVENT; - /** - * The remote service version the customer side will reference - */ - protected String version; - /** - * The remote service group the customer side will reference - */ - protected String group; /** * declares which app or service this interface belongs to @@ -212,21 +204,7 @@ public abstract class AbstractReferenceConfig extends AbstractInterfaceConfig { this.sticky = sticky; } - 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; - } @Parameter(key = "provided-by") public String getProvidedBy() { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java index a95a16f740..25b9d9bf03 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java @@ -112,11 +112,6 @@ public class ApplicationConfig extends AbstractConfig { */ private MonitorConfig monitor; - /** - * Is default or not - */ - private Boolean isDefault; - /** * Directory for saving thread dump */ @@ -320,14 +315,6 @@ public class ApplicationConfig extends AbstractConfig { LoggerFactory.setLoggerAdapter(logger); } - public Boolean isDefault() { - return isDefault; - } - - public void setDefault(Boolean isDefault) { - this.isDefault = isDefault; - } - @Parameter(key = DUMP_DIRECTORY) public String getDumpDirectory() { return dumpDirectory; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java b/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java index 894f13819f..cfc45d9122 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java @@ -119,4 +119,6 @@ public interface Constants { String MULTI_SERIALIZATION_KEY = "serialize.multiple"; String[] DOT_COMPATIBLE_KEYS = new String[]{"qos-enable", "qos-port", "qos-accept-foreign-ip"}; + + String IGNORE_CHECK_KEYS = "ignoreCheckKeys"; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ConsumerConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ConsumerConfig.java index 25f2c77f44..53e63d29bf 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ConsumerConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ConsumerConfig.java @@ -27,11 +27,6 @@ public class ConsumerConfig extends AbstractReferenceConfig { private static final long serialVersionUID = 2827274711143680600L; - /** - * Whether to use the default protocol - */ - private Boolean isDefault; - /** * Networking framework client uses: netty, mina, etc. */ @@ -72,11 +67,7 @@ public class ConsumerConfig extends AbstractReferenceConfig { System.setProperty("sun.rmi.transport.tcp.responseTimeout", String.valueOf(timeout)); } } - - public Boolean isDefault() { - return isDefault; - } - + public String getClient() { return client; } @@ -93,14 +84,6 @@ public class ConsumerConfig extends AbstractReferenceConfig { this.threadpool = threadpool; } - public Boolean getDefault() { - return isDefault; - } - - public void setDefault(Boolean isDefault) { - this.isDefault = isDefault; - } - public Integer getCorethreads() { return corethreads; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/MetadataReportConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/MetadataReportConfig.java index 0be508902f..41874886ed 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/MetadataReportConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/MetadataReportConfig.java @@ -85,6 +85,12 @@ public class MetadataReportConfig extends AbstractConfig { */ private String registry; + /** + * File for saving metadata center dynamic list + */ + private String file; + + public MetadataReportConfig() { } @@ -225,4 +231,12 @@ public class MetadataReportConfig extends AbstractConfig { public void setRegistry(String registry) { this.registry = registry; } + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/MethodConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/MethodConfig.java index d239c4a37d..968b61f22a 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/MethodConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/MethodConfig.java @@ -139,15 +139,28 @@ public class MethodConfig extends AbstractMethodConfig { this.setReturn(method.isReturn()); - //TODO callback method processing is not completed - if(!"".equals(method.oninvoke())){ - this.setOninvoke(method.oninvoke()); + String split = "."; + + if (!"".equals(method.oninvoke()) && method.oninvoke().lastIndexOf(split) > 0) { + int index = method.oninvoke().lastIndexOf(split); + String ref = method.oninvoke().substring(0, index); + String methodName = method.oninvoke().substring(index + 1); + this.setOninvoke(ref); + this.setOninvokeMethod(methodName); } - if(!"".equals(method.onreturn())){ - this.setOnreturn(method.onreturn()); + if (!"".equals(method.onreturn()) && method.onreturn().lastIndexOf(split) > 0) { + int index = method.onreturn().lastIndexOf(split); + String ref = method.onreturn().substring(0, index); + String methodName = method.onreturn().substring(index + 1); + this.setOnreturn(ref); + this.setOnreturnMethod(methodName); } - if(!"".equals(method.onthrow())){ - this.setOnthrow(method.onthrow()); + if (!"".equals(method.onthrow()) && method.onthrow().lastIndexOf(split) > 0) { + int index = method.onthrow().lastIndexOf(split); + String ref = method.onthrow().substring(0, index); + String methodName = method.onthrow().substring(index + 1); + this.setOnthrow(ref); + this.setOnthrowMethod(methodName); } if (method.arguments() != null && method.arguments().length != 0) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ModuleConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ModuleConfig.java index 064310bf1a..310401323d 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ModuleConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ModuleConfig.java @@ -62,11 +62,6 @@ public class ModuleConfig extends AbstractConfig { */ private MonitorConfig monitor; - /** - * If it's default - */ - private Boolean isDefault; - public ModuleConfig() { } @@ -142,12 +137,4 @@ public class ModuleConfig extends AbstractConfig { this.monitor = new MonitorConfig(monitor); } - public Boolean isDefault() { - return isDefault; - } - - public void setDefault(Boolean isDefault) { - this.isDefault = isDefault; - } - } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/MonitorConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/MonitorConfig.java index 8bd7bedee3..ed8beb1383 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/MonitorConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/MonitorConfig.java @@ -63,11 +63,6 @@ public class MonitorConfig extends AbstractConfig { */ private Map parameters; - /** - * If it's default - */ - private Boolean isDefault; - public MonitorConfig() { } @@ -135,14 +130,6 @@ public class MonitorConfig extends AbstractConfig { this.parameters = parameters; } - public Boolean isDefault() { - return isDefault; - } - - public void setDefault(Boolean isDefault) { - this.isDefault = isDefault; - } - public String getInterval() { return interval; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ProtocolConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ProtocolConfig.java index 25cca82479..b8af63cab2 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ProtocolConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ProtocolConfig.java @@ -79,6 +79,11 @@ public class ProtocolConfig extends AbstractConfig { */ private Integer iothreads; + /** + * Thread pool keepAliveTime, default unit TimeUnit.MILLISECONDS + */ + private Integer alive; + /** * Thread pool's queue length */ @@ -193,11 +198,6 @@ public class ProtocolConfig extends AbstractConfig { */ private Map parameters; - /** - * If it's default - */ - private Boolean isDefault; - private Boolean sslEnabled; public ProtocolConfig() { @@ -300,6 +300,14 @@ public class ProtocolConfig extends AbstractConfig { this.iothreads = iothreads; } + public Integer getAlive() { + return alive; + } + + public void setAlive(Integer alive) { + this.alive = alive; + } + public Integer getQueues() { return queues; } @@ -482,14 +490,6 @@ public class ProtocolConfig extends AbstractConfig { this.parameters = parameters; } - public Boolean isDefault() { - return isDefault; - } - - public void setDefault(Boolean isDefault) { - this.isDefault = isDefault; - } - @Parameter(key = SSL_ENABLED_KEY) public Boolean getSslEnabled() { return sslEnabled; @@ -540,4 +540,46 @@ public class ProtocolConfig extends AbstractConfig { public boolean isValid() { return StringUtils.isNotEmpty(name); } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ProtocolConfig{"); + sb.append("name='").append(name).append('\''); + sb.append(", host='").append(host).append('\''); + sb.append(", port=").append(port); + sb.append(", contextpath='").append(contextpath).append('\''); + sb.append(", threadpool='").append(threadpool).append('\''); + sb.append(", threadname='").append(threadname).append('\''); + sb.append(", corethreads=").append(corethreads); + sb.append(", threads=").append(threads); + sb.append(", iothreads=").append(iothreads); + sb.append(", alive=").append(alive); + sb.append(", queues=").append(queues); + sb.append(", accepts=").append(accepts); + sb.append(", codec='").append(codec).append('\''); + sb.append(", serialization='").append(serialization).append('\''); + sb.append(", charset='").append(charset).append('\''); + sb.append(", payload=").append(payload); + sb.append(", buffer=").append(buffer); + sb.append(", heartbeat=").append(heartbeat); + sb.append(", accesslog='").append(accesslog).append('\''); + sb.append(", transporter='").append(transporter).append('\''); + sb.append(", exchanger='").append(exchanger).append('\''); + sb.append(", dispatcher='").append(dispatcher).append('\''); + sb.append(", networker='").append(networker).append('\''); + sb.append(", server='").append(server).append('\''); + sb.append(", client='").append(client).append('\''); + sb.append(", telnet='").append(telnet).append('\''); + sb.append(", prompt='").append(prompt).append('\''); + sb.append(", status='").append(status).append('\''); + sb.append(", register=").append(register); + sb.append(", keepAlive=").append(keepAlive); + sb.append(", optimizer='").append(optimizer).append('\''); + sb.append(", extension='").append(extension).append('\''); + sb.append(", parameters=").append(parameters); + sb.append(", isDefault=").append(isDefault); + sb.append(", sslEnabled=").append(sslEnabled); + sb.append('}'); + return sb.toString(); + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ProviderConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ProviderConfig.java index 7b38a75515..62eb377086 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ProviderConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ProviderConfig.java @@ -69,6 +69,11 @@ public class ProviderConfig extends AbstractServiceConfig { */ private Integer iothreads; + /** + * Thread pool keepAliveTime, default unit TimeUnit.MILLISECONDS + */ + private Integer alive; + /** * Thread pool queue length */ @@ -149,26 +154,17 @@ public class ProviderConfig extends AbstractServiceConfig { */ private Integer wait; - /** - * Whether to use the default protocol - */ - private Boolean isDefault; - @Deprecated public void setProtocol(String protocol) { this.protocols = new ArrayList<>(Arrays.asList(new ProtocolConfig(protocol))); } + @Override @Parameter(excluded = true) public Boolean isDefault() { return isDefault; } - @Deprecated - public void setDefault(Boolean isDefault) { - this.isDefault = isDefault; - } - @Parameter(excluded = true) public String getHost() { return host; @@ -240,6 +236,14 @@ public class ProviderConfig extends AbstractServiceConfig { this.iothreads = iothreads; } + public Integer getAlive() { + return alive; + } + + public void setAlive(Integer alive) { + this.alive = alive; + } + public Integer getQueues() { return queues; } @@ -425,4 +429,35 @@ public class ProviderConfig extends AbstractServiceConfig { this.wait = wait; } + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ProviderConfig{"); + sb.append("host='").append(host).append('\''); + sb.append(", port=").append(port); + sb.append(", contextpath='").append(contextpath).append('\''); + sb.append(", threadpool='").append(threadpool).append('\''); + sb.append(", threadname='").append(threadname).append('\''); + sb.append(", threads=").append(threads); + sb.append(", iothreads=").append(iothreads); + sb.append(", alive=").append(alive); + sb.append(", queues=").append(queues); + sb.append(", accepts=").append(accepts); + sb.append(", codec='").append(codec).append('\''); + sb.append(", charset='").append(charset).append('\''); + sb.append(", payload=").append(payload); + sb.append(", buffer=").append(buffer); + sb.append(", transporter='").append(transporter).append('\''); + sb.append(", exchanger='").append(exchanger).append('\''); + sb.append(", dispatcher='").append(dispatcher).append('\''); + sb.append(", networker='").append(networker).append('\''); + sb.append(", server='").append(server).append('\''); + sb.append(", client='").append(client).append('\''); + sb.append(", telnet='").append(telnet).append('\''); + sb.append(", prompt='").append(prompt).append('\''); + sb.append(", status='").append(status).append('\''); + sb.append(", wait=").append(wait); + sb.append(", isDefault=").append(isDefault); + sb.append('}'); + return sb.toString(); + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java index e52b060f3c..e5ab4c35c2 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java @@ -42,11 +42,6 @@ public abstract class ReferenceConfigBase extends AbstractReferenceConfig { private static final long serialVersionUID = -5864351140409987595L; - /** - * The interface name of the reference service - */ - protected String interfaceName; - /** * The interface class of the reference service */ @@ -72,7 +67,6 @@ public abstract class ReferenceConfigBase extends AbstractReferenceConfig { */ protected String protocol; - protected ServiceMetadata serviceMetadata; public ReferenceConfigBase() { serviceMetadata = new ServiceMetadata(); @@ -268,12 +262,14 @@ public abstract class ReferenceConfigBase extends AbstractReferenceConfig { @Override protected void computeValidRegistryIds() { - super.computeValidRegistryIds(); - if (StringUtils.isEmpty(getRegistryIds())) { - if (getConsumer() != null && StringUtils.isNotEmpty(getConsumer().getRegistryIds())) { - setRegistryIds(getConsumer().getRegistryIds()); + if (consumer != null) { + if (notHasSelfRegistryProperty()) { + setRegistries(consumer.getRegistries()); + setRegistryIds(consumer.getRegistryIds()); } } + + super.computeValidRegistryIds(); } @Parameter(excluded = true) diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java index c84f20aaf4..9fcdec2ec3 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java @@ -139,11 +139,6 @@ public class RegistryConfig extends AbstractConfig { */ private Map parameters; - /** - * Whether it's default - */ - private Boolean isDefault; - /** * Simple the registry. both useful for provider and consumer * @@ -456,14 +451,6 @@ public class RegistryConfig extends AbstractConfig { } } - public Boolean isDefault() { - return isDefault; - } - - public void setDefault(Boolean isDefault) { - this.isDefault = isDefault; - } - public Boolean getSimplified() { return simplified; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ServiceConfigBase.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ServiceConfigBase.java index f1b14d6df6..e6705efc7e 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ServiceConfigBase.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ServiceConfigBase.java @@ -43,10 +43,7 @@ public abstract class ServiceConfigBase extends AbstractServiceConfig { private static final long serialVersionUID = 3033787999037024738L; - /** - * The interface name of the exported service - */ - protected String interfaceName; + /** * The interface class of the exported service @@ -78,7 +75,7 @@ public abstract class ServiceConfigBase extends AbstractServiceConfig { */ protected volatile String generic; - protected ServiceMetadata serviceMetadata; + public ServiceConfigBase() { serviceMetadata = new ServiceMetadata(); @@ -202,32 +199,35 @@ public abstract class ServiceConfigBase extends AbstractServiceConfig { } public void checkProtocol() { + if (provider != null && notHasSelfProtocolProperty()) { + setProtocols(provider.getProtocols()); + setProtocolIds(provider.getProtocolIds()); + } + if (CollectionUtils.isEmpty(protocols) && provider != null) { setProtocols(provider.getProtocols()); } convertProtocolIdsToProtocols(); } + private boolean notHasSelfProtocolProperty() { + return CollectionUtils.isEmpty(protocols) && StringUtils.isEmpty(protocolIds); + } + public void completeCompoundConfigs() { super.completeCompoundConfigs(provider); if (provider != null) { - if (protocols == null) { + if (notHasSelfProtocolProperty()) { setProtocols(provider.getProtocols()); + setProtocolIds(provider.getProtocolIds()); } if (configCenter == null) { setConfigCenter(provider.getConfigCenter()); } - if (StringUtils.isEmpty(registryIds)) { - setRegistryIds(provider.getRegistryIds()); - } - if (StringUtils.isEmpty(protocolIds)) { - setProtocolIds(provider.getProtocolIds()); - } } } private void convertProtocolIdsToProtocols() { - computeValidProtocolIds(); if (StringUtils.isEmpty(protocolIds)) { if (CollectionUtils.isEmpty(protocols)) { List protocolConfigs = ApplicationModel.getConfigManager().getDefaultProtocols(); @@ -292,9 +292,7 @@ public abstract class ServiceConfigBase extends AbstractServiceConfig { setInterface(interfaceClass); } - public String getInterface() { - return interfaceName; - } + public void setInterface(Class interfaceClass) { if (interfaceClass != null && !interfaceClass.isInterface()) { @@ -304,14 +302,6 @@ public abstract class ServiceConfigBase extends AbstractServiceConfig { setInterface(interfaceClass == null ? null : interfaceClass.getName()); } - public void setInterface(String interfaceName) { - this.interfaceName = interfaceName; - // FIXME, add id strategy in ConfigManager -// if (StringUtils.isEmpty(id)) { -// id = interfaceName; -// } - } - public T getRef() { return ref; } @@ -413,22 +403,14 @@ public abstract class ServiceConfigBase extends AbstractServiceConfig { return StringUtils.isEmpty(this.version) ? (provider != null ? provider.getVersion() : this.version) : this.version; } - private void computeValidProtocolIds() { - if (StringUtils.isEmpty(getProtocolIds())) { - if (getProvider() != null && StringUtils.isNotEmpty(getProvider().getProtocolIds())) { - setProtocolIds(getProvider().getProtocolIds()); - } - } - } - @Override protected void computeValidRegistryIds() { - super.computeValidRegistryIds(); - if (StringUtils.isEmpty(getRegistryIds())) { - if (getProvider() != null && StringUtils.isNotEmpty(getProvider().getRegistryIds())) { - setRegistryIds(getProvider().getRegistryIds()); - } + if (provider != null && notHasSelfRegistryProperty()) { + setRegistries(provider.getRegistries()); + setRegistryIds(provider.getRegistryIds()); } + + super.computeValidRegistryIds(); } public abstract void export(); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/SslConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/SslConfig.java index 3be7d3ef5c..83f86594c7 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/SslConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/SslConfig.java @@ -18,10 +18,10 @@ package org.apache.dubbo.config; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.IOUtils; import org.apache.dubbo.config.support.Parameter; -import java.io.FileInputStream; -import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; import java.util.concurrent.atomic.AtomicBoolean; @@ -120,9 +120,9 @@ public class SslConfig extends AbstractConfig { this.clientTrustCertCollectionPath = clientTrustCertCollectionPath; } - public InputStream getServerKeyCertChainPathStream() throws FileNotFoundException { + public InputStream getServerKeyCertChainPathStream() throws IOException { if (serverKeyCertChainPath != null) { - serverKeyCertChainPathStream = new FileInputStream(serverKeyCertChainPath); + serverKeyCertChainPathStream = IOUtils.getURL(serverKeyCertChainPath).openStream(); } return serverKeyCertChainPathStream; } @@ -131,9 +131,9 @@ public class SslConfig extends AbstractConfig { this.serverKeyCertChainPathStream = serverKeyCertChainPathStream; } - public InputStream getServerPrivateKeyPathStream() throws FileNotFoundException { + public InputStream getServerPrivateKeyPathStream() throws IOException { if (serverPrivateKeyPath != null) { - serverPrivateKeyPathStream = new FileInputStream(serverPrivateKeyPath); + serverPrivateKeyPathStream = IOUtils.getURL(serverPrivateKeyPath).openStream(); } return serverPrivateKeyPathStream; } @@ -142,9 +142,9 @@ public class SslConfig extends AbstractConfig { this.serverPrivateKeyPathStream = serverPrivateKeyPathStream; } - public InputStream getServerTrustCertCollectionPathStream() throws FileNotFoundException { + public InputStream getServerTrustCertCollectionPathStream() throws IOException { if (serverTrustCertCollectionPath != null) { - serverTrustCertCollectionPathStream = new FileInputStream(serverTrustCertCollectionPath); + serverTrustCertCollectionPathStream = IOUtils.getURL(serverTrustCertCollectionPath).openStream(); } return serverTrustCertCollectionPathStream; } @@ -153,9 +153,9 @@ public class SslConfig extends AbstractConfig { this.serverTrustCertCollectionPathStream = serverTrustCertCollectionPathStream; } - public InputStream getClientKeyCertChainPathStream() throws FileNotFoundException { + public InputStream getClientKeyCertChainPathStream() throws IOException { if (clientKeyCertChainPath != null) { - clientKeyCertChainPathStream = new FileInputStream(clientKeyCertChainPath); + clientKeyCertChainPathStream = IOUtils.getURL(clientKeyCertChainPath).openStream(); } return clientKeyCertChainPathStream; } @@ -164,9 +164,9 @@ public class SslConfig extends AbstractConfig { this.clientKeyCertChainPathStream = clientKeyCertChainPathStream; } - public InputStream getClientPrivateKeyPathStream() throws FileNotFoundException { + public InputStream getClientPrivateKeyPathStream() throws IOException { if (clientPrivateKeyPath != null) { - clientPrivateKeyPathStream = new FileInputStream(clientPrivateKeyPath); + clientPrivateKeyPathStream = IOUtils.getURL(clientPrivateKeyPath).openStream(); } return clientPrivateKeyPathStream; } @@ -175,9 +175,9 @@ public class SslConfig extends AbstractConfig { this.clientPrivateKeyPathStream = clientPrivateKeyPathStream; } - public InputStream getClientTrustCertCollectionPathStream() throws FileNotFoundException { + public InputStream getClientTrustCertCollectionPathStream() throws IOException { if (clientTrustCertCollectionPath != null) { - clientTrustCertCollectionPathStream = new FileInputStream(clientTrustCertCollectionPath); + clientTrustCertCollectionPathStream = IOUtils.getURL(clientTrustCertCollectionPath).openStream(); } return clientTrustCertCollectionPathStream; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboReference.java b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboReference.java index 404c45499c..44fbab586c 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboReference.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboReference.java @@ -16,6 +16,8 @@ */ package org.apache.dubbo.config.annotation; +import org.apache.dubbo.common.constants.ClusterRules; +import org.apache.dubbo.common.constants.LoadbalanceRules; import org.apache.dubbo.common.constants.RegistryConstants; import org.apache.dubbo.config.ReferenceConfigBase; @@ -163,8 +165,9 @@ public @interface DubboReference { /** * Cluster strategy, legal values include: failover, failfast, failsafe, failback, forking + * you can use {@link org.apache.dubbo.common.constants.ClusterRules#FAIL_FAST} …… */ - String cluster() default ""; + String cluster() default ClusterRules.EMPTY; /** * Maximum connections service provider can accept, default value is 0 - connection is shared @@ -207,10 +210,9 @@ public @interface DubboReference { /** * Load balance strategy, legal values include: random, roundrobin, leastactive - *

    - * see Constants#DEFAULT_LOADBALANCE + * you can use {@link org.apache.dubbo.common.constants.LoadbalanceRules#RANDOM} …… */ - String loadbalance() default ""; + String loadbalance() default LoadbalanceRules.EMPTY; /** * Whether to enable async invocation, default value is false diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboService.java b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboService.java index 33178edf4c..3eeaa71444 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboService.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboService.java @@ -17,6 +17,8 @@ package org.apache.dubbo.config.annotation; +import org.apache.dubbo.common.constants.ClusterRules; + import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; @@ -123,8 +125,9 @@ public @interface DubboService { /** * Cluster strategy, legal values include: failover, failfast, failsafe, failback, forking + * you can use {@link org.apache.dubbo.common.constants.ClusterRules#FAIL_FAST} …… */ - String cluster() default ""; + String cluster() default ClusterRules.EMPTY; /** * How the proxy is generated, legal values include: jdk, javassist @@ -173,9 +176,9 @@ public @interface DubboService { /** * Load balance strategy, legal values include: random, roundrobin, leastactive * - * @see org.apache.dubbo.common.constants.CommonConstants#DEFAULT_LOADBALANCE + * you can use {@link org.apache.dubbo.common.constants.LoadbalanceRules#RANDOM} …… */ - String loadbalance() default ""; + String loadbalance() default ClusterRules.EMPTY; /** * Whether to enable async invocation, default value is false diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigConfigurationAdapter.java b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigConfigurationAdapter.java index dca7978b2e..3251f63f3b 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigConfigurationAdapter.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigConfigurationAdapter.java @@ -32,7 +32,7 @@ public class ConfigConfigurationAdapter implements Configuration { public ConfigConfigurationAdapter(AbstractConfig config) { Map configMetadata = config.getMetaData(); - metaData = new HashMap<>(configMetadata.size()); + metaData = new HashMap<>(configMetadata.size(), 1.0f); for (Map.Entry entry : configMetadata.entrySet()) { String prefix = config.getPrefix().endsWith(".") ? config.getPrefix() : config.getPrefix() + "."; String id = StringUtils.isEmpty(config.getId()) ? "" : config.getId() + "."; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java index e62ef97bd2..39dacbb8b9 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java @@ -46,6 +46,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -386,9 +387,7 @@ public class ConfigManager extends LifecycleAdapter implements FrameworkExt { } public void clear() { - write(() -> { - this.configsCache.clear(); - }); + write(this.configsCache::clear); } /** @@ -442,6 +441,18 @@ public class ConfigManager extends LifecycleAdapter implements FrameworkExt { // throw new IllegalStateException("No such " + configType.getName() + " is found"); return null; } else if (size > 1) { + AtomicReference defaultConfig = new AtomicReference<>(); + configsMap.forEach((str, config) -> { + if (Boolean.TRUE.equals(config.isDefault())) { + defaultConfig.compareAndSet(null, config); + } + }); + + if (defaultConfig.get() != null) { + return defaultConfig.get(); + } + + if (configWarnLogEnabled && warnLogStatus.get(configType) == null) { logger.warn("Expected single matching of " + configType + ", but found " + size + " instances, will randomly pick the first one."); warnLogStatus.put(configType, true); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/event/DirectEventDispatcher.java b/dubbo-common/src/main/java/org/apache/dubbo/event/DirectEventDispatcher.java index 773f5321c1..de14ef2253 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/event/DirectEventDispatcher.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/event/DirectEventDispatcher.java @@ -23,6 +23,7 @@ package org.apache.dubbo.event; * @since 2.7.5 */ public final class DirectEventDispatcher extends AbstractEventDispatcher { + public static final String NAME = "direct"; public DirectEventDispatcher() { super(DIRECT_EXECUTOR); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/event/EventDispatcher.java b/dubbo-common/src/main/java/org/apache/dubbo/event/EventDispatcher.java index 53e08135d3..f6f2631465 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/event/EventDispatcher.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/event/EventDispatcher.java @@ -29,7 +29,7 @@ import java.util.concurrent.Executor; * @see DirectEventDispatcher * @since 2.7.5 */ -@SPI("direct") +@SPI(DirectEventDispatcher.NAME) public interface EventDispatcher extends Listenable> { /** diff --git a/dubbo-common/src/main/java/org/apache/dubbo/event/ParallelEventDispatcher.java b/dubbo-common/src/main/java/org/apache/dubbo/event/ParallelEventDispatcher.java index 082bb99211..7a7e725aa7 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/event/ParallelEventDispatcher.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/event/ParallelEventDispatcher.java @@ -25,6 +25,7 @@ import java.util.concurrent.ForkJoinPool; * @since 2.7.5 */ public class ParallelEventDispatcher extends AbstractEventDispatcher { + public static final String NAME = "parallel"; public ParallelEventDispatcher() { super(ForkJoinPool.commonPool()); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/support/ProtocolUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/support/ProtocolUtils.java index 85920e213d..7b983beed4 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/support/ProtocolUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/support/ProtocolUtils.java @@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentMap; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_RAW_RETURN; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_BEAN; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_DEFAULT; +import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_GSON; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_NATIVE_JAVA; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_PROTOBUF; @@ -56,6 +57,7 @@ public class ProtocolUtils { || GENERIC_SERIALIZATION_NATIVE_JAVA.equalsIgnoreCase(generic) /* Streaming generalization call supporting jdk serialization */ || GENERIC_SERIALIZATION_BEAN.equalsIgnoreCase(generic) || GENERIC_SERIALIZATION_PROTOBUF.equalsIgnoreCase(generic) + || GENERIC_SERIALIZATION_GSON.equalsIgnoreCase(generic) || GENERIC_RAW_RETURN.equalsIgnoreCase(generic)); } @@ -75,6 +77,11 @@ public class ProtocolUtils { && GENERIC_SERIALIZATION_NATIVE_JAVA.equalsIgnoreCase(generic); } + public static boolean isGsonGenericSerialization(String generic) { + return isGeneric(generic) + && GENERIC_SERIALIZATION_GSON.equalsIgnoreCase(generic); + } + public static boolean isBeanGenericSerialization(String generic) { return isGeneric(generic) && GENERIC_SERIALIZATION_BEAN.equals(generic); } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/BaseServiceMetadataTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/BaseServiceMetadataTest.java new file mode 100644 index 0000000000..1075aec70d --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/BaseServiceMetadataTest.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.common; + +import org.junit.jupiter.api.Test; + +import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_VERSION; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class BaseServiceMetadataTest { + + @Test + public void test() { + BaseServiceMetadata baseServiceMetadata = new BaseServiceMetadata(); + baseServiceMetadata.setGroup("group1"); + baseServiceMetadata.setServiceInterfaceName("org.apache.dubbo.common.TestInterface"); + baseServiceMetadata.setVersion("1.0.0"); + baseServiceMetadata.setServiceKey(BaseServiceMetadata.buildServiceKey("org.apache.dubbo.common.TestInterface", "group1", "1.0.0")); + + assertEquals(baseServiceMetadata.getGroup(), "group1"); + assertEquals(baseServiceMetadata.getServiceInterfaceName(), "org.apache.dubbo.common.TestInterface"); + assertEquals(baseServiceMetadata.getVersion(), "1.0.0"); + assertEquals(baseServiceMetadata.getServiceKey(), "group1/org.apache.dubbo.common.TestInterface:1.0.0"); + assertEquals(baseServiceMetadata.getDisplayServiceKey(), "org.apache.dubbo.common.TestInterface:1.0.0"); + + baseServiceMetadata.setServiceKey(BaseServiceMetadata.buildServiceKey("org.apache.dubbo.common.TestInterface", null, null)); + assertEquals(baseServiceMetadata.getServiceKey(), "org.apache.dubbo.common.TestInterface"); + baseServiceMetadata.setServiceKey(BaseServiceMetadata.buildServiceKey("org.apache.dubbo.common.TestInterface", "", "")); + assertEquals(baseServiceMetadata.getServiceKey(), "org.apache.dubbo.common.TestInterface"); + + + baseServiceMetadata.setVersion("2.0.0"); + baseServiceMetadata.generateServiceKey(); + assertEquals(baseServiceMetadata.getServiceKey(), "group1/org.apache.dubbo.common.TestInterface:2.0.0"); + + assertEquals(BaseServiceMetadata.versionFromServiceKey("group1/org.apache.dubbo.common.TestInterface:1.0.0"), "1.0.0"); + assertEquals(BaseServiceMetadata.groupFromServiceKey("group1/org.apache.dubbo.common.TestInterface:1.0.0"), "group1"); + assertEquals(BaseServiceMetadata.interfaceFromServiceKey("group1/org.apache.dubbo.common.TestInterface:1.0.0"), "org.apache.dubbo.common.TestInterface"); + + assertEquals(DEFAULT_VERSION, BaseServiceMetadata.versionFromServiceKey("")); + assertNull(BaseServiceMetadata.groupFromServiceKey("")); + assertEquals(BaseServiceMetadata.interfaceFromServiceKey(""), ""); + + assertEquals(BaseServiceMetadata.revertDisplayServiceKey("org.apache.dubbo.common.TestInterface:1.0.0").getDisplayServiceKey(), + "org.apache.dubbo.common.TestInterface:1.0.0"); + assertEquals(BaseServiceMetadata.revertDisplayServiceKey("org.apache.dubbo.common.TestInterface").getDisplayServiceKey(), + "org.apache.dubbo.common.TestInterface:null"); + assertEquals(BaseServiceMetadata.revertDisplayServiceKey(null).getDisplayServiceKey(),"null:null"); + assertEquals(BaseServiceMetadata.revertDisplayServiceKey("org.apache.dubbo.common.TestInterface:1.0.0:1").getDisplayServiceKey(),"null:null"); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/PojoUtilsForNonPublicStaticTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/PojoUtilsForNonPublicStaticTest.java new file mode 100644 index 0000000000..8e1e5a569d --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/PojoUtilsForNonPublicStaticTest.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common; + +import org.apache.dubbo.common.utils.PojoUtils; + +import org.junit.jupiter.api.Test; + +public class PojoUtilsForNonPublicStaticTest { + + @Test + public void testNonPublicStaticClass() { + NonPublicStaticData nonPublicStaticData = new NonPublicStaticData("horizon"); + PojoUtils.generalize(nonPublicStaticData); + } + + /** + * the static class need is not same package with PojoUtils, so define it here. + */ + static class NonPublicStaticData { + + private String name; + + public NonPublicStaticData(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java index 3ca62ce83c..f295de9f57 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java @@ -16,8 +16,13 @@ */ package org.apache.dubbo.common; +import net.bytebuddy.utility.RandomString; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.HashSet; +import java.util.Set; + import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; @@ -25,16 +30,53 @@ import static org.hamcrest.MatcherAssert.assertThat; * Created by LinShunkang on 2020/03/12 */ public class URLStrParserTest { + private static Set testCases = new HashSet<>(16); + private static Set errorDecodedCases = new HashSet<>(8); + private static Set errorEncodedCases = new HashSet<>(8); + + static { + testCases.add("dubbo://192.168.1.1"); + testCases.add("dubbo://192.168.1.1?"); + testCases.add("dubbo://127.0.0.1?test=中文测试"); + testCases.add("dubbo://admin:admin123@192.168.1.41:28113/org.test.api.DemoService$Iface?anyhost=true&application=demo-service&dubbo=2.6.1&generic=false&interface=org.test.api.DemoService$Iface&methods=orbCompare,checkText,checkPicture&pid=65557&revision=1.4.17&service.filter=bootMetrics&side=provider&status=server&threads=200×tamp=1583136298859&version=1.0.0"); + // super long text test + testCases.add("dubbo://192.168.1.1/" + RandomString.make(10240)); + testCases.add("file:/path/to/file.txt"); + testCases.add("dubbo://fe80:0:0:0:894:aeec:f37d:23e1%en0/path?abc=abc"); + + errorDecodedCases.add("dubbo:192.168.1.1"); + errorDecodedCases.add("://192.168.1.1"); + errorDecodedCases.add(":/192.168.1.1"); + + errorEncodedCases.add("dubbo%3a%2f%2f192.168.1.41%3fabc%3"); + errorEncodedCases.add("dubbo%3a192.168.1.1%3fabc%3dabc"); + errorEncodedCases.add("%3a%2f%2f192.168.1.1%3fabc%3dabc"); + errorEncodedCases.add("%3a%2f192.168.1.1%3fabc%3dabc"); + errorEncodedCases.add("dubbo%3a%2f%2f127.0.0.1%3ftest%3d%e2%96%b2%e2%96%bc%e2%97%80%e2%96%b6%e2%86%90%e2%86%91%e2%86%92%e2%86%93%e2%86%94%e2%86%95%e2%88%9e%c2%b1%e9%be%98%e9%9d%90%e9%bd%89%9%d%b"); + } @Test - public void test() { - String str = "dubbo%3A%2F%2Fadmin%3Aadmin123%40192.168.1.41%3A28113%2Forg.test.api.DemoService%24Iface%3Fanyhost%3Dtrue%26application%3Ddemo-service%26dubbo%3D2.6.1%26generic%3Dfalse%26interface%3Dorg.test.api.DemoService%24Iface%26methods%3DorbCompare%2CcheckText%2CcheckPicture%26pid%3D65557%26revision%3D1.4.17%26service.filter%3DbootMetrics%26side%3Dprovider%26status%3Dserver%26threads%3D200%26timestamp%3D1583136298859%26version%3D1.0.0"; - System.out.println(URLStrParser.parseEncodedStr(str)); + public void testEncoded() { + testCases.forEach(testCase -> { + assertThat(URLStrParser.parseEncodedStr(URL.encode(testCase)), equalTo(URL.valueOf(testCase))); + }); - String decodeStr = URL.decode(str); - URL originalUrl = URL.valueOf(decodeStr); - assertThat(URLStrParser.parseEncodedStr(str), equalTo(originalUrl)); - assertThat(URLStrParser.parseDecodedStr(decodeStr), equalTo(originalUrl)); + errorEncodedCases.forEach(errorCase -> { + Assertions.assertThrows(RuntimeException.class, + () -> URLStrParser.parseEncodedStr(errorCase)); + }); + } + + @Test + public void testDecoded() { + testCases.forEach(testCase -> { + assertThat(URLStrParser.parseDecodedStr(testCase), equalTo(URL.valueOf(testCase))); + }); + + errorDecodedCases.forEach(errorCase -> { + Assertions.assertThrows(RuntimeException.class, + () -> URLStrParser.parseDecodedStr(errorCase)); + }); } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java index e8932c37b8..a34045e62d 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java @@ -32,6 +32,8 @@ import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -929,5 +931,115 @@ public class URLTest { URL url5 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&weight=10&group=group&version=1.0.0"); Assertions.assertEquals(url4, url5); + + URL url6 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=false&dubbo=2.0.2&generic=true&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=1599556506417"); + URL url7 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=false&dubbo=2.0.2&generic=true&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=2299556506417"); + assertEquals(url6, url7); + + URL url8 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=false&dubbo=2.0.2&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=2299556506417"); + assertNotEquals(url7, url8); + + URL url9 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=true&dubbo=2.0.2&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=2299556506417"); + assertNotEquals(url8, url9); + } + + + @Test + public void testEqualsWithPassword() { + URL url1 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + URL url2 = URL.valueOf("ad@min:hello@4321@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + URL url3 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + + boolean actual1 = url1.equals(url2); + boolean actual2 = url1.equals(url3); + assertFalse(actual1); + assertTrue(actual2); + } + + @Test + public void testEqualsWithPath() { + URL url1 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path1?version=1.0.0&application=morgan"); + URL url2 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path2?version=1.0.0&application=morgan"); + URL url3 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path1?version=1.0.0&application=morgan"); + + boolean actual1 = url1.equals(url2); + boolean actual2 = url1.equals(url3); + assertFalse(actual1); + assertTrue(actual2); + } + + @Test + public void testEqualsWithPort() { + URL url1 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + URL url2 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20881/context/path?version=1.0.0&application=morgan"); + URL url3 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + + boolean actual1 = url1.equals(url2); + boolean actual2 = url1.equals(url3); + assertFalse(actual1); + assertTrue(actual2); + } + + @Test + public void testEqualsWithProtocol() { + URL url1 = URL.valueOf("dubbo://ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + URL url2 = URL.valueOf("file://ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + URL url3 = URL.valueOf("dubbo://ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + + boolean actual1 = url1.equals(url2); + boolean actual2 = url1.equals(url3); + assertFalse(actual1); + assertTrue(actual2); + } + + @Test + public void testEqualsWithUser() { + URL url1 = URL.valueOf("ad@min1:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + URL url2 = URL.valueOf("ad@min2:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + URL url3 = URL.valueOf("ad@min1:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + + boolean actual1 = url1.equals(url2); + boolean actual2 = url1.equals(url3); + assertFalse(actual1); + assertTrue(actual2); + } + @Test + public void testHashcode() { + URL url1 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=false&dubbo=2.0.2&generic=true&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=1599556506417"); + URL url2 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=false&dubbo=2.0.2&generic=true&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=2299556506417"); + assertEquals(url1.hashCode(), url2.hashCode()); + + URL url3 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=false&dubbo=2.0.2&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=2299556506417"); + assertNotEquals(url2.hashCode(), url3.hashCode()); + + URL url4 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=true&dubbo=2.0.2&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=2299556506417"); + assertNotEquals(url3.hashCode(), url4.hashCode()); + } + + @Test + public void testParameterContainPound() { + URL url = URL.valueOf( + "dubbo://ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan£=abcd#efg&protocol=registry"); + Assertions.assertEquals("abcd#efg", url.getParameter("pound")); + Assertions.assertEquals("registry", url.getParameter("protocol")); + } + + } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/WrapperTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/WrapperTest.java index d3c4d906a0..cc246f8ae9 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/WrapperTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/WrapperTest.java @@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; public class WrapperTest { @@ -113,6 +114,26 @@ public class WrapperTest { }); } + @Test + public void testOverloadMethod() throws Exception { + Wrapper w = Wrapper.getWrapper(I2.class); + assertEquals(2, w.getMethodNames().length); + + Impl2 impl = new Impl2(); + + w.invokeMethod(impl, "setFloat", new Class[]{float.class}, new Object[]{1F}); + assertEquals(1F, impl.getFloat1()); + assertNull(impl.getFloat2()); + + w.invokeMethod(impl, "setFloat", new Class[]{Float.class}, new Object[]{2f}); + assertEquals(1F, impl.getFloat1()); + assertEquals(2F, impl.getFloat2()); + + w.invokeMethod(impl, "setFloat", new Class[]{Float.class}, new Object[]{null}); + assertEquals(1F, impl.getFloat1()); + assertNull(impl.getFloat2()); + } + @Test public void test_getDeclaredMethodNames_ContainExtendsParentMethods() throws Exception { assertArrayEquals(new String[]{"hello",}, Wrapper.getWrapper(Parent1.class).getMethodNames()); @@ -141,6 +162,12 @@ public class WrapperTest { void setFloat(float f); } + public interface I2 { + void setFloat(float f); + + void setFloat(Float f); + } + public interface EmptyService { } @@ -191,6 +218,29 @@ public class WrapperTest { } } + public static class Impl2 implements I2 { + private float float1; + private Float float2; + + @Override + public void setFloat(float f) { + this.float1 = f; + } + + @Override + public void setFloat(Float f) { + this.float2 = f; + } + + public float getFloat1() { + return float1; + } + + public Float getFloat2() { + return float2; + } + } + public static class EmptyServiceImpl implements EmptyService { } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/JdkCompilerTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/JdkCompilerTest.java index b95e645680..e0bc3a9820 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/JdkCompilerTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/JdkCompilerTest.java @@ -53,4 +53,35 @@ public class JdkCompilerTest extends JavaCodeTest { Assertions.assertEquals("Hello world!", sayHello.invoke(instance)); }); } + + @Test + public void test_compileJavaClass_java8() throws Exception { + JdkCompiler compiler = new JdkCompiler("1.8"); + Class clazz = compiler.compile(getSimpleCode(), JdkCompiler.class.getClassLoader()); + Object instance = clazz.newInstance(); + Method sayHello = instance.getClass().getMethod("sayHello"); + Assertions.assertEquals("Hello world!", sayHello.invoke(instance)); + } + + @Test + public void test_compileJavaClass0_java8() throws Exception { + Assertions.assertThrows(IllegalStateException.class, () -> { + JdkCompiler compiler = new JdkCompiler("1.8"); + Class clazz = compiler.compile(getSimpleCodeWithoutPackage(), JdkCompiler.class.getClassLoader()); + Object instance = clazz.newInstance(); + Method sayHello = instance.getClass().getMethod("sayHello"); + Assertions.assertEquals("Hello world!", sayHello.invoke(instance)); + }); + } + + @Test + public void test_compileJavaClass1_java8() throws Exception { + Assertions.assertThrows(IllegalStateException.class, () -> { + JdkCompiler compiler = new JdkCompiler("1.8"); + Class clazz = compiler.compile(getSimpleCodeWithSyntax(), JdkCompiler.class.getClassLoader()); + Object instance = clazz.newInstance(); + Method sayHello = instance.getClass().getMethod("sayHello"); + Assertions.assertEquals("Hello world!", sayHello.invoke(instance)); + }); + } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEventTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEventTest.java index 91549275d4..7725399ab2 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEventTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEventTest.java @@ -28,28 +28,28 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; */ public class ConfigChangedEventTest { - private static final String key = "k"; + private static final String KEY = "k"; - private static final String group = "g"; + private static final String GROUP = "g"; - private static final String content = "c"; + private static final String CONTENT = "c"; @Test public void testGetter() { - ConfigChangedEvent event = new ConfigChangedEvent(key, group, content); + ConfigChangedEvent event = new ConfigChangedEvent(KEY, GROUP, CONTENT); - assertEquals(key, event.getKey()); - assertEquals(group, event.getGroup()); - assertEquals(content, event.getContent()); + assertEquals(KEY, event.getKey()); + assertEquals(GROUP, event.getGroup()); + assertEquals(CONTENT, event.getContent()); assertEquals(ConfigChangeType.MODIFIED, event.getChangeType()); assertEquals("k,g", event.getSource()); - event = new ConfigChangedEvent(key, group, content, ConfigChangeType.ADDED); + event = new ConfigChangedEvent(KEY, GROUP, CONTENT, ConfigChangeType.ADDED); - assertEquals(key, event.getKey()); - assertEquals(group, event.getGroup()); - assertEquals(content, event.getContent()); + assertEquals(KEY, event.getKey()); + assertEquals(GROUP, event.getGroup()); + assertEquals(CONTENT, event.getContent()); assertEquals(ConfigChangeType.ADDED, event.getChangeType()); assertEquals("k,g", event.getSource()); } @@ -57,15 +57,15 @@ public class ConfigChangedEventTest { @Test public void testEqualsAndHashCode() { for (ConfigChangeType type : ConfigChangeType.values()) { - assertEquals(new ConfigChangedEvent(key, group, content, type), new ConfigChangedEvent(key, group, content, type)); - assertEquals(new ConfigChangedEvent(key, group, content, type).hashCode(), new ConfigChangedEvent(key, group, content, type).hashCode()); - assertEquals(new ConfigChangedEvent(key, group, content, type).toString(), new ConfigChangedEvent(key, group, content, type).toString()); + assertEquals(new ConfigChangedEvent(KEY, GROUP, CONTENT, type), new ConfigChangedEvent(KEY, GROUP, CONTENT, type)); + assertEquals(new ConfigChangedEvent(KEY, GROUP, CONTENT, type).hashCode(), new ConfigChangedEvent(KEY, GROUP, CONTENT, type).hashCode()); + assertEquals(new ConfigChangedEvent(KEY, GROUP, CONTENT, type).toString(), new ConfigChangedEvent(KEY, GROUP, CONTENT, type).toString()); } } @Test public void testToString() { - ConfigChangedEvent event = new ConfigChangedEvent(key, group, content); + ConfigChangedEvent event = new ConfigChangedEvent(KEY, GROUP, CONTENT); assertNotNull(event.toString()); } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java index 746cab7f0b..e3be4f13ab 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java @@ -32,6 +32,8 @@ import org.apache.dubbo.common.extension.activate.impl.ValueActivateExtImpl; import org.apache.dubbo.common.extension.convert.String2BooleanConverter; import org.apache.dubbo.common.extension.convert.String2DoubleConverter; import org.apache.dubbo.common.extension.convert.String2IntegerConverter; +import org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt; +import org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt; import org.apache.dubbo.common.extension.ext1.SimpleExt; import org.apache.dubbo.common.extension.ext1.impl.SimpleExtImpl1; import org.apache.dubbo.common.extension.ext1.impl.SimpleExtImpl2; @@ -256,6 +258,7 @@ public class ExtensionLoaderTest { assertThat(ext, instanceOf(AddExt1_ManualAdd1.class)); assertEquals("Manual1", getExtensionLoader(AddExt1.class).getExtensionName(AddExt1_ManualAdd1.class)); + ExtensionLoader.resetExtensionLoader(AddExt1.class); } @Test @@ -325,6 +328,7 @@ public class ExtensionLoaderTest { assertThat(ext, instanceOf(AddExt1_ManualAdd2.class)); assertEquals("impl1", getExtensionLoader(AddExt1.class).getExtensionName(AddExt1_ManualAdd2.class)); } + ExtensionLoader.resetExtensionLoader(AddExt1.class); } @Test @@ -338,6 +342,7 @@ public class ExtensionLoaderTest { adaptive = loader.getAdaptiveExtension(); assertTrue(adaptive instanceof AddExt3_ManualAdaptive); + ExtensionLoader.resetExtensionLoader(AddExt3.class); } @Test @@ -375,7 +380,7 @@ public class ExtensionLoaderTest { fail(); } catch (IllegalStateException expected) { assertThat(expected.getMessage(), containsString("Failed to load extension class (interface: interface org.apache.dubbo.common.extension.ext7.InitErrorExt")); - assertThat(expected.getCause(), instanceOf(ExceptionInInitializerError.class)); + assertThat(expected.getMessage(), containsString("java.lang.ExceptionInInitializerError")); } } @@ -531,4 +536,88 @@ public class ExtensionLoaderTest { assertEquals(ServicesLoadingStrategy.class, loadingStrategy.getClass()); assertEquals(Prioritized.MIN_PRIORITY, loadingStrategy.getPriority()); } + + @Test + public void testDuplicatedImplWithoutOverriddenStrategy() { + List loadingStrategies = ExtensionLoader.getLoadingStrategies(); + ExtensionLoader.setLoadingStrategies(new DubboExternalLoadingStrategyTest(false), + new DubboInternalLoadingStrategyTest(false)); + ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(DuplicatedWithoutOverriddenExt.class); + try { + extensionLoader.getExtension("duplicated"); + fail(); + } catch (IllegalStateException expected) { + assertThat(expected.getMessage(), containsString("Failed to load extension class (interface: interface org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt")); + assertThat(expected.getMessage(), containsString("cause: Duplicate extension org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt name duplicated")); + }finally { + //recover the loading strategies + ExtensionLoader.setLoadingStrategies(loadingStrategies.toArray(new LoadingStrategy[loadingStrategies.size()])); + } + } + + @Test + public void testDuplicatedImplWithOverriddenStrategy() { + List loadingStrategies = ExtensionLoader.getLoadingStrategies(); + ExtensionLoader.setLoadingStrategies(new DubboExternalLoadingStrategyTest(true), + new DubboInternalLoadingStrategyTest(true)); + ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(DuplicatedOverriddenExt.class); + DuplicatedOverriddenExt duplicatedOverriddenExt = extensionLoader.getExtension("duplicated"); + assertEquals("DuplicatedOverriddenExt1", duplicatedOverriddenExt.echo()); + //recover the loading strategies + ExtensionLoader.setLoadingStrategies(loadingStrategies.toArray(new LoadingStrategy[loadingStrategies.size()])); + } + + /** + * The external {@link LoadingStrategy}, which can set if it support overridden + */ + private static class DubboExternalLoadingStrategyTest implements LoadingStrategy { + + public DubboExternalLoadingStrategyTest(boolean overridden) { + this.overridden = overridden; + } + + private boolean overridden; + + @Override + public String directory() { + return "META-INF/dubbo/external/"; + } + + @Override + public boolean overridden() { + return this.overridden; + } + + @Override + public int getPriority() { + return MAX_PRIORITY + 1; + } + } + + /** + * The internal {@link LoadingStrategy}, which can set if it support overridden + */ + private static class DubboInternalLoadingStrategyTest implements LoadingStrategy { + + public DubboInternalLoadingStrategyTest(boolean overridden) { + this.overridden = overridden; + } + + private boolean overridden; + + @Override + public String directory() { + return "META-INF/dubbo/internal/"; + } + + @Override + public boolean overridden() { + return this.overridden; + } + + @Override + public int getPriority() { + return MAX_PRIORITY; + } + } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/DuplicatedOverriddenExt.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/DuplicatedOverriddenExt.java new file mode 100644 index 0000000000..628be2150a --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/DuplicatedOverriddenExt.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.extension.duplicated; + +import org.apache.dubbo.common.extension.SPI; + +/** + * This is an interface for testing duplicated extension + */ +@SPI +public interface DuplicatedOverriddenExt { + + String echo(); +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/DuplicatedWithoutOverriddenExt.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/DuplicatedWithoutOverriddenExt.java new file mode 100644 index 0000000000..5a0cc0b414 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/DuplicatedWithoutOverriddenExt.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.extension.duplicated; + +import org.apache.dubbo.common.extension.SPI; + +/** + * This is an interface for testing duplicated extension + * see issue: https://github.com/apache/dubbo/issues/3575 + */ +@SPI +public interface DuplicatedWithoutOverriddenExt { + + String echo(); +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedOverriddenExt1.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedOverriddenExt1.java new file mode 100644 index 0000000000..99e4db5fba --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedOverriddenExt1.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.extension.duplicated.impl; + +import org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt; + +public class DuplicatedOverriddenExt1 implements DuplicatedOverriddenExt { + + @Override + public String echo() { + return "DuplicatedOverriddenExt1"; + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedOverriddenExt2.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedOverriddenExt2.java new file mode 100644 index 0000000000..2c9fd4e10a --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedOverriddenExt2.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.extension.duplicated.impl; + +import org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt; + +public class DuplicatedOverriddenExt2 implements DuplicatedOverriddenExt { + + @Override + public String echo() { + return "DuplicatedOverriddenExt2"; + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedWithoutOverriddenExt1.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedWithoutOverriddenExt1.java new file mode 100644 index 0000000000..ce08a86b8b --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedWithoutOverriddenExt1.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.extension.duplicated.impl; + +import org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt; + +public class DuplicatedWithoutOverriddenExt1 implements DuplicatedWithoutOverriddenExt { + + @Override + public String echo() { + return "DuplicatedWithoutOverriddenExt1"; + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedWithoutOverriddenExt2.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedWithoutOverriddenExt2.java new file mode 100644 index 0000000000..1aed1e5620 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/duplicated/impl/DuplicatedWithoutOverriddenExt2.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.extension.duplicated.impl; + +import org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt; + +public class DuplicatedWithoutOverriddenExt2 implements DuplicatedWithoutOverriddenExt { + + @Override + public String echo() { + return "DuplicatedWithoutOverriddenExt2"; + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/Demo.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/Demo.java new file mode 100644 index 0000000000..b51e3e1df4 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/Demo.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.extension.wrapper; + +import org.apache.dubbo.common.extension.SPI; + +@SPI("demo") +public interface Demo { + String echo(String msg); +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/WrapperTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/WrapperTest.java new file mode 100644 index 0000000000..a6f1a56e8f --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/WrapperTest.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.extension.wrapper; + +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.extension.wrapper.impl.DemoWrapper; +import org.apache.dubbo.common.extension.wrapper.impl.DemoWrapper2; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link org.apache.dubbo.common.extension.Wrapper} Test + * + * @since 2.7.5 + */ +public class WrapperTest { + + @Test + public void testWrapper() { + Demo demoWrapper = ExtensionLoader.getExtensionLoader(Demo.class).getExtension("demo"); + assertTrue(demoWrapper instanceof DemoWrapper); + Demo demoWrapper2 = ExtensionLoader.getExtensionLoader(Demo.class).getExtension("demo2"); + assertTrue(demoWrapper2 instanceof DemoWrapper2); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoImpl.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoImpl.java new file mode 100644 index 0000000000..abcd5e687c --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoImpl.java @@ -0,0 +1,26 @@ +/* + * 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.extension.wrapper.impl; + +import org.apache.dubbo.common.extension.wrapper.Demo; + +public class DemoImpl implements Demo { + @Override + public String echo(String msg) { + return msg; + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper.java new file mode 100644 index 0000000000..44ec737a1f --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper.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.extension.wrapper.impl; + +import org.apache.dubbo.common.extension.Wrapper; +import org.apache.dubbo.common.extension.wrapper.Demo; + +@Wrapper(matches = {"demo"}, mismatches = "demo2") +public class DemoWrapper implements Demo { + private Demo demo; + + public DemoWrapper(Demo demo) { + this.demo = demo; + } + + public String echo(String msg) { + return demo.echo(msg); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper2.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper2.java new file mode 100644 index 0000000000..c8e4385474 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper2.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.extension.wrapper.impl; + +import org.apache.dubbo.common.extension.Wrapper; +import org.apache.dubbo.common.extension.wrapper.Demo; + +@Wrapper(matches = {"demo2"}, mismatches = {"demo"}) +public class DemoWrapper2 implements Demo { + private Demo demo; + + public DemoWrapper2(Demo demo) { + this.demo = demo; + } + + public String echo(String msg) { + return demo.echo(msg); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/model/SerializablePerson.java b/dubbo-common/src/test/java/org/apache/dubbo/common/model/SerializablePerson.java index d78c5928ea..730d2cb319 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/model/SerializablePerson.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/model/SerializablePerson.java @@ -27,6 +27,9 @@ public class SerializablePerson implements Serializable { private String[] value = {"value1", "value2"}; + public SerializablePerson(char description , boolean adult){ + + } public String getName() { return name; } @@ -94,4 +97,4 @@ public class SerializablePerson implements Serializable { return false; return true; } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadlocal/InternalThreadLocalTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadlocal/InternalThreadLocalTest.java index df0b908a6c..8df34f8e35 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadlocal/InternalThreadLocalTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadlocal/InternalThreadLocalTest.java @@ -79,6 +79,7 @@ public class InternalThreadLocalTest { final InternalThreadLocal internalThreadLocalString = new InternalThreadLocal(); internalThreadLocalString.set("value"); Assertions.assertEquals(2, InternalThreadLocal.size(), "size method is wrong!"); + InternalThreadLocal.removeAll(); } @Test diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/ThreadlessExecutorTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/ThreadlessExecutorTest.java new file mode 100644 index 0000000000..a17b462de0 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/ThreadlessExecutorTest.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.common.threadpool; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; + +public class ThreadlessExecutorTest { + private static ThreadlessExecutor executor; + + static { + URL url = URL.valueOf("dubbo://127.0.0.1:12345"); + ExecutorService sharedExecutor = + ExtensionLoader.getExtensionLoader(ExecutorRepository.class) + .getDefaultExtension().createExecutorIfAbsent(url); + executor = new ThreadlessExecutor(sharedExecutor); + } + + @Test + public void test() throws InterruptedException { + for (int i = 0; i < 10; i++) { + executor.execute(()->{throw new RuntimeException("test");}); + } + + CompletableFuture stubFuture = new CompletableFuture<>(); + executor.setWaitingFuture(stubFuture); + Assertions.assertEquals(executor.getWaitingFuture(),stubFuture); + + executor.waitAndDrain(); + + executor.execute(()->{}); + + executor.waitAndDrain(); + + executor.shutdown(); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepositoryTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepositoryTest.java new file mode 100644 index 0000000000..bb3fe4ea97 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepositoryTest.java @@ -0,0 +1,79 @@ +/* + * 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.threadpool.manager; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; + +public class ExecutorRepositoryTest { + private ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension(); + + @Test + public void testGetExecutor() { + testGet(URL.valueOf("dubbo://127.0.0.1:23456")); + testGet(URL.valueOf("dubbo://127.0.0.1:23456?side=consumer")); + + Assertions.assertNotNull(executorRepository.getSharedExecutor()); + Assertions.assertNotNull(executorRepository.getServiceExporterExecutor()); + executorRepository.nextScheduledExecutor(); + } + + private void testGet(URL url) { + Assertions.assertNull(executorRepository.getExecutor(url)); + + ExecutorService executorService = executorRepository.createExecutorIfAbsent(url); + executorService.shutdown(); + executorService = executorRepository.createExecutorIfAbsent(url); + Assertions.assertFalse(executorService.isShutdown()); + + Assertions.assertEquals(executorService, executorRepository.getExecutor(url)); + executorService.shutdown(); + Assertions.assertNotEquals(executorService, executorRepository.getExecutor(url)); + } + + @Test + public void testUpdateExecutor() { + URL url = URL.valueOf("dubbo://127.0.0.1:23456?threads=5"); + ThreadPoolExecutor executorService = (ThreadPoolExecutor) executorRepository.createExecutorIfAbsent(url); + + executorService.setCorePoolSize(3); + executorRepository.updateThreadpool(url, executorService); + + executorService.setCorePoolSize(3); + executorService.setMaximumPoolSize(3); + executorRepository.updateThreadpool(url, executorService); + + executorService.setMaximumPoolSize(20); + executorService.setCorePoolSize(10); + executorRepository.updateThreadpool(url, executorService); + + executorService.setCorePoolSize(10); + executorService.setMaximumPoolSize(10); + executorRepository.updateThreadpool(url, executorService); + + executorService.setCorePoolSize(5); + executorRepository.updateThreadpool(url, executorService); + + + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReportTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReportTest.java index ea54e9f02b..4add905f2c 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReportTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReportTest.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL; import org.junit.jupiter.api.Test; +import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadPoolExecutor; @@ -30,6 +31,25 @@ public class AbortPolicyWithReportTest { URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?dump.directory=/tmp&version=1.0.0&application=morgan&noValue="); AbortPolicyWithReport abortPolicyWithReport = new AbortPolicyWithReport("Test", url); + try { + abortPolicyWithReport.rejectedExecution(() -> System.out.println("hello"), (ThreadPoolExecutor) Executors.newFixedThreadPool(1)); + } catch (RejectedExecutionException rj) { + // ignore + } + + Thread.sleep(1000); + + } + + @Test + public void jStackDumpTest_dumpDirectoryNotExists_cannotBeCreatedTakeUserHome() throws InterruptedException { + final String dumpDirectory = dumpDirectoryCannotBeCreated(); + + URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?dump.directory=" + + dumpDirectory + + "&version=1.0.0&application=morgan&noValue=true"); + AbortPolicyWithReport abortPolicyWithReport = new AbortPolicyWithReport("Test", url); + try { abortPolicyWithReport.rejectedExecution(new Runnable() { @Override @@ -42,6 +62,38 @@ public class AbortPolicyWithReportTest { } Thread.sleep(1000); + } + private String dumpDirectoryCannotBeCreated() { + final String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + // "con" is one of Windows reserved names, https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file + return "con"; + } else { + return "/dev/full/" + UUID.randomUUID().toString(); + } + } + + @Test + public void jStackDumpTest_dumpDirectoryNotExists_canBeCreated() throws InterruptedException { + final String dumpDirectory = UUID.randomUUID().toString(); + + URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?dump.directory=" + + dumpDirectory + + "&version=1.0.0&application=morgan&noValue=true"); + AbortPolicyWithReport abortPolicyWithReport = new AbortPolicyWithReport("Test", url); + + try { + abortPolicyWithReport.rejectedExecution(new Runnable() { + @Override + public void run() { + System.out.println("hello"); + } + }, (ThreadPoolExecutor) Executors.newFixedThreadPool(1)); + } catch (RejectedExecutionException rj) { + // ignore + } + + Thread.sleep(1000); } } \ No newline at end of file diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/cached/CachedThreadPoolTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/cached/CachedThreadPoolTest.java index 2754635f0e..f345970fb0 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/cached/CachedThreadPoolTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/cached/CachedThreadPoolTest.java @@ -60,14 +60,11 @@ public class CachedThreadPoolTest { Matchers.instanceOf(AbortPolicyWithReport.class)); final CountDownLatch latch = new CountDownLatch(1); - executor.execute(new Runnable() { - @Override - public void run() { - Thread thread = Thread.currentThread(); - assertThat(thread, instanceOf(InternalThread.class)); - assertThat(thread.getName(), startsWith("demo")); - latch.countDown(); - } + executor.execute(() -> { + Thread thread = Thread.currentThread(); + assertThat(thread, instanceOf(InternalThread.class)); + assertThat(thread.getName(), startsWith("demo")); + latch.countDown(); }); latch.await(); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolExecutorTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolExecutorTest.java index 7846c0e91a..f8fdbfb7e2 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolExecutorTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolExecutorTest.java @@ -76,16 +76,13 @@ public class EagerThreadPoolExecutorTest { for (int i = 0; i < 15; i++) { Thread.sleep(50); - executor.execute(new Runnable() { - @Override - public void run() { - System.out.println("thread number in current pool:" + executor.getPoolSize() + ", task number in task queue:" + executor.getQueue() - .size() + " executor size: " + executor.getPoolSize()); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } + executor.execute(() -> { + System.out.println("thread number in current pool:" + executor.getPoolSize() + ", task number in task queue:" + executor.getQueue() + .size() + " executor size: " + executor.getPoolSize()); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); } }); } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolTest.java index 95e41875bc..cb8e9e2661 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolTest.java @@ -62,14 +62,11 @@ public class EagerThreadPoolTest { Matchers.instanceOf(AbortPolicyWithReport.class)); final CountDownLatch latch = new CountDownLatch(1); - executor.execute(new Runnable() { - @Override - public void run() { - Thread thread = Thread.currentThread(); - assertThat(thread, instanceOf(InternalThread.class)); - assertThat(thread.getName(), startsWith("demo")); - latch.countDown(); - } + executor.execute(() -> { + Thread thread = Thread.currentThread(); + assertThat(thread, instanceOf(InternalThread.class)); + assertThat(thread.getName(), startsWith("demo")); + latch.countDown(); }); latch.await(); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/timer/HashedWheelTimerTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/timer/HashedWheelTimerTest.java index 022351462b..9b9c5ee120 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/timer/HashedWheelTimerTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/timer/HashedWheelTimerTest.java @@ -18,55 +18,181 @@ package org.apache.dubbo.common.timer; import org.apache.dubbo.common.utils.NamedThreadFactory; + +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +import java.lang.ref.WeakReference; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class HashedWheelTimerTest { + private CountDownLatch tryStopTaskCountDownLatch = new CountDownLatch(1); + private CountDownLatch errorTaskCountDownLatch = new CountDownLatch(1); - private class PrintTask implements TimerTask { + private static class EmptyTask implements TimerTask { + @Override + public void run(Timeout timeout) { + } + } + + private static class BlockTask implements TimerTask { + @Override + public void run(Timeout timeout) throws InterruptedException { + this.wait(); + } + } + + private class ErrorTask implements TimerTask { + @Override + public void run(Timeout timeout) { + errorTaskCountDownLatch.countDown(); + throw new RuntimeException("Test"); + } + } + + private class TryStopTask implements TimerTask { + private Timer timer; + + public TryStopTask(Timer timer) { + this.timer = timer; + } @Override public void run(Timeout timeout) { - final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - System.out.println("task :" + LocalDateTime.now().format(formatter)); + Assertions.assertThrows(RuntimeException.class, () -> timer.stop()); + tryStopTaskCountDownLatch.countDown(); } } @Test - public void newTimeout() throws InterruptedException { - final Timer timer = newTimer(); - for (int i = 0; i < 10; i++) { - timer.newTimeout(new PrintTask(), 1, TimeUnit.SECONDS); - Thread.sleep(1000); + public void constructorTest() { + // use weak reference to let gc work every time + // which can check finalize method and reduce memory usage in time + WeakReference timer = new WeakReference<>(new HashedWheelTimer()); + timer = new WeakReference<>(new HashedWheelTimer(100, TimeUnit.MILLISECONDS)); + timer = new WeakReference<>(new HashedWheelTimer(100, TimeUnit.MILLISECONDS, 8)); + + // to cover arg check branches + Assertions.assertThrows(RuntimeException.class, () -> { + new HashedWheelTimer( + null, + 100, + TimeUnit.MILLISECONDS, + 8, -1); + }); + + Assertions.assertThrows(RuntimeException.class, () -> { + new HashedWheelTimer( + new NamedThreadFactory("dubbo-future-timeout", true), + 0, + TimeUnit.MILLISECONDS, + 8, -1); + }); + + Assertions.assertThrows(RuntimeException.class, () -> { + new HashedWheelTimer( + new NamedThreadFactory("dubbo-future-timeout", true), + 100, + null, + 8, -1); + }); + + Assertions.assertThrows(RuntimeException.class, () -> { + new HashedWheelTimer( + new NamedThreadFactory("dubbo-future-timeout", true), + 100, + TimeUnit.MILLISECONDS, + 0, -1); + }); + + Assertions.assertThrows(RuntimeException.class, () -> { + new HashedWheelTimer( + new NamedThreadFactory("dubbo-future-timeout", true), + Long.MAX_VALUE, + TimeUnit.MILLISECONDS, + 8, -1); + }); + + Assertions.assertThrows(RuntimeException.class, () -> { + new HashedWheelTimer( + new NamedThreadFactory("dubbo-future-timeout", true), + 100, + TimeUnit.MILLISECONDS, + Integer.MAX_VALUE, -1); + }); + + for (int i = 0; i < 128; i++) { + // to trigger INSTANCE_COUNT_LIMIT + timer = new WeakReference<>(new HashedWheelTimer()); } - Thread.sleep(5000); + + System.gc(); } @Test - public void stop() throws InterruptedException { - final Timer timer = newTimer(); - for (int i = 0; i < 10; i++) { - timer.newTimeout(new PrintTask(), 5, TimeUnit.SECONDS); - Thread.sleep(100); - } - //stop timer - timer.stop(); - - try { - //this will throw a exception - timer.newTimeout(new PrintTask(), 5, TimeUnit.SECONDS); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private Timer newTimer() { - return new HashedWheelTimer( + public void createTaskTest() throws InterruptedException { + HashedWheelTimer timer = new HashedWheelTimer( new NamedThreadFactory("dubbo-future-timeout", true), - 100, - TimeUnit.MILLISECONDS); + 10, + TimeUnit.MILLISECONDS, + 8, 8); + + Assertions.assertThrows(RuntimeException.class, + () -> timer.newTimeout(null, 5, TimeUnit.SECONDS)); + Assertions.assertThrows(RuntimeException.class, + () -> timer.newTimeout(new EmptyTask(), 5, null)); + + Timeout timeout = timer.newTimeout(new ErrorTask(), 10, TimeUnit.MILLISECONDS); + errorTaskCountDownLatch.await(); + Assertions.assertFalse(timeout.cancel()); + Assertions.assertFalse(timeout.isCancelled()); + Assertions.assertNotNull(timeout.toString()); + Assertions.assertEquals(timeout.timer(), timer); + + timeout = timer.newTimeout(new EmptyTask(), 1000, TimeUnit.SECONDS); + timeout.cancel(); + Assertions.assertTrue(timeout.isCancelled()); + + List timeouts = new LinkedList<>(); + for (; timer.pendingTimeouts() < 8; ) { + // to trigger maxPendingTimeouts + timeout = timer.newTimeout(new BlockTask(), -1, TimeUnit.MILLISECONDS); + timeouts.add(timeout); + Assertions.assertNotNull(timeout.toString()); + } + Assertions.assertEquals(timer.pendingTimeouts(), 8); + + // this will throw an exception because of maxPendingTimeouts + Assertions.assertThrows(RuntimeException.class, + () -> timer.newTimeout(new BlockTask(), 1, TimeUnit.MILLISECONDS)); + + timeout = timeouts.get(2); + // wait until the task expired + Thread.sleep(100); + Assertions.assertTrue(timeout.isExpired()); + + timer.stop(); + } + + @Test + public void stopTaskTest() throws InterruptedException { + Timer timer = new HashedWheelTimer(new NamedThreadFactory("dubbo-future-timeout", true)); + timer.newTimeout(new TryStopTask(timer), 10, TimeUnit.MILLISECONDS); + tryStopTaskCountDownLatch.await(); + + for (int i = 0; i < 8; i++) { + timer.newTimeout(new EmptyTask(), 0, TimeUnit.SECONDS); + } + // stop timer + timer.stop(); + Assertions.assertTrue(timer.isStop()); + + // this will throw an exception + Assertions.assertThrows(RuntimeException.class, + () -> timer.newTimeout(new EmptyTask(), 5, TimeUnit.SECONDS)); + } } \ No newline at end of file diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CollectionUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CollectionUtilsTest.java index 36af51d5d7..d65e92ea0f 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CollectionUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CollectionUtilsTest.java @@ -141,7 +141,7 @@ public class CollectionUtilsTest { @Test public void testJoinList() throws Exception { - List list = Arrays.asList(); + List list = emptyList(); assertEquals("", CollectionUtils.join(list, "/")); list = Arrays.asList("x"); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CompatibleTypeUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CompatibleTypeUtilsTest.java index 85e7180120..6a838c7cb7 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CompatibleTypeUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CompatibleTypeUtilsTest.java @@ -84,7 +84,7 @@ public class CompatibleTypeUtilsTest { result = CompatibleTypeUtils.compatibleTypeConvert("2011-12-11T12:24:12.047", java.time.LocalTime.class); assertEquals(DateTimeFormatter.ofPattern("HH:mm:ss").format((java.time.LocalTime) result), "12:24:12"); - result = CompatibleTypeUtils.compatibleTypeConvert("2011-12-11T12:24:12.047", java.time.LocalDate.class); + result = CompatibleTypeUtils.compatibleTypeConvert("2011-12-11", java.time.LocalDate.class); assertEquals(DateTimeFormatter.ofPattern("yyyy-MM-dd").format((java.time.LocalDate) result), "2011-12-11"); result = CompatibleTypeUtils.compatibleTypeConvert("ab", char[].class); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ConfigUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ConfigUtilsTest.java index 1f82a5271f..2324e27eb2 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ConfigUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ConfigUtilsTest.java @@ -91,7 +91,7 @@ public class ConfigUtilsTest { @Test public void testMergeValuesDeleteDefault() { List merged = ConfigUtils.mergeValues(ThreadPool.class, "-default", asList("fixed", "default.limited", "cached")); - assertEquals(asList(), merged); + assertEquals(Collections.emptyList(), merged); } @Test diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultPageTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultPageTest.java index 36ceb4d2df..41c0e03ff3 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultPageTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultPageTest.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.common.utils; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.List; @@ -32,6 +33,12 @@ public class DefaultPageTest { @Test public void test() { List data = asList(1, 2, 3, 4, 5); - DefaultPage page = new DefaultPage(0, 1, data.subList(0, 1), data.size()); + DefaultPage page = new DefaultPage<>(0, 1, data.subList(0, 1), data.size()); + Assertions.assertEquals(page.getOffset(), 0); + Assertions.assertEquals(page.getPageSize(), 1); + Assertions.assertEquals(page.getTotalSize(), data.size()); + Assertions.assertEquals(page.getData(), data.subList(0, 1)); + Assertions.assertEquals(page.getTotalPages(), 5); + Assertions.assertTrue(page.hasNext()); } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LFUCacheTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LFUCacheTest.java index 34535241b4..d7a725089e 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LFUCacheTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LFUCacheTest.java @@ -16,16 +16,19 @@ */ package org.apache.dubbo.common.utils; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import static org.hamcrest.Matchers.equalTo; +import java.io.IOException; + import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; public class LFUCacheTest { @Test public void testCacheEviction() throws Exception { - LFUCache cache = new LFUCache(8, 0.8f); + LFUCache cache = new LFUCache<>(8, 0.8f); cache.put("one", 1); cache.put("two", 2); cache.put("three", 3); @@ -46,7 +49,7 @@ public class LFUCacheTest { @Test public void testCacheRemove() throws Exception { - LFUCache cache = new LFUCache(8, 0.8f); + LFUCache cache = new LFUCache<>(8, 0.8f); cache.put("one", 1); cache.put("two", 2); cache.put("three", 3); @@ -65,8 +68,16 @@ public class LFUCacheTest { } @Test - public void testCapacity() throws Exception { - LFUCache cache = new LFUCache(); + public void testDefaultCapacity() throws Exception { + LFUCache cache = new LFUCache<>(); assertThat(cache.getCapacity(), equalTo(1000)); } + + @Test + public void testErrorConstructArguments() throws IOException { + Assertions.assertThrows(IllegalArgumentException.class, () -> new LFUCache<>(0, 0.8f)); + Assertions.assertThrows(IllegalArgumentException.class, () -> new LFUCache<>(-1, 0.8f)); + Assertions.assertThrows(IllegalArgumentException.class, () -> new LFUCache<>(100, 0.0f)); + Assertions.assertThrows(IllegalArgumentException.class, () -> new LFUCache<>(100, -0.1f)); + } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LogTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LogTest.java index 203cadb547..39b60ab849 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LogTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LogTest.java @@ -18,39 +18,68 @@ package org.apache.dubbo.common.utils; import org.apache.log4j.Level; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; -import static org.hamcrest.MatcherAssert.assertThat; public class LogTest { @Test public void testLogName() throws Exception { - Log log = new Log(); - log.setLogName("log-name"); - assertThat(log.getLogName(), equalTo("log-name")); + Log log1 = new Log(); + Log log2 = new Log(); + Log log3 = new Log(); + log1.setLogName("log-name"); + log2.setLogName("log-name"); + log3.setLogName("log-name-other"); + assertThat(log1.getLogName(), equalTo("log-name")); + Assertions.assertEquals(log1, log2); + Assertions.assertEquals(log1.hashCode(), log2.hashCode()); + Assertions.assertNotEquals(log1, log3); } @Test public void testLogLevel() throws Exception { - Log log = new Log(); - log.setLogLevel(Level.ALL); - assertThat(log.getLogLevel(), is(Level.ALL)); + Log log1 = new Log(); + Log log2 = new Log(); + Log log3 = new Log(); + log1.setLogLevel(Level.ALL); + log2.setLogLevel(Level.ALL); + log3.setLogLevel(Level.DEBUG); + assertThat(log1.getLogLevel(), is(Level.ALL)); + Assertions.assertEquals(log1, log2); + Assertions.assertEquals(log1.hashCode(), log2.hashCode()); + Assertions.assertNotEquals(log1, log3); } @Test public void testLogMessage() throws Exception { - Log log = new Log(); - log.setLogMessage("log-message"); - assertThat(log.getLogMessage(), equalTo("log-message")); + Log log1 = new Log(); + Log log2 = new Log(); + Log log3 = new Log(); + log1.setLogMessage("log-message"); + log2.setLogMessage("log-message"); + log3.setLogMessage("log-message-other"); + assertThat(log1.getLogMessage(), equalTo("log-message")); + Assertions.assertEquals(log1, log2); + Assertions.assertEquals(log1.hashCode(), log2.hashCode()); + Assertions.assertNotEquals(log1, log3); } @Test public void testLogThread() throws Exception { - Log log = new Log(); - log.setLogThread("log-thread"); - assertThat(log.getLogThread(), equalTo("log-thread")); + Log log1 = new Log(); + Log log2 = new Log(); + Log log3 = new Log(); + log1.setLogThread("log-thread"); + log2.setLogThread("log-thread"); + log3.setLogThread("log-thread-other"); + assertThat(log1.getLogThread(), equalTo("log-thread")); + Assertions.assertEquals(log1, log2); + Assertions.assertEquals(log1.hashCode(), log2.hashCode()); + Assertions.assertNotEquals(log1, log3); } } 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 index 051363b981..a5a78cc765 100644 --- 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 @@ -18,6 +18,8 @@ package org.apache.dubbo.common.utils; import org.junit.jupiter.api.Test; +import static org.apache.dubbo.common.utils.MemberUtils.isPrivate; +import static org.apache.dubbo.common.utils.MemberUtils.isPublic; 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; @@ -34,9 +36,19 @@ public class MemberUtilsTest { assertFalse(isStatic(getClass().getMethod("testIsStatic"))); assertTrue(isStatic(getClass().getMethod("staticMethod"))); + assertTrue(isPrivate(getClass().getDeclaredMethod("privateMethod"))); + assertTrue(isPublic(getClass().getMethod("publicMethod"))); } public static void staticMethod() { } + + private void privateMethod() { + + } + + public void publicMethod() { + + } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MethodUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MethodUtilsTest.java index 7cb143eee8..e8eb7f1c1b 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MethodUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MethodUtilsTest.java @@ -21,6 +21,17 @@ import org.junit.jupiter.api.Test; import java.lang.reflect.Method; +import static org.apache.dubbo.common.utils.MethodUtils.excludedDeclaredClass; +import static org.apache.dubbo.common.utils.MethodUtils.findMethod; +import static org.apache.dubbo.common.utils.MethodUtils.findNearestOverriddenMethod; +import static org.apache.dubbo.common.utils.MethodUtils.findOverriddenMethod; +import static org.apache.dubbo.common.utils.MethodUtils.getAllDeclaredMethods; +import static org.apache.dubbo.common.utils.MethodUtils.getAllMethods; +import static org.apache.dubbo.common.utils.MethodUtils.getDeclaredMethods; +import static org.apache.dubbo.common.utils.MethodUtils.getMethods; +import static org.apache.dubbo.common.utils.MethodUtils.invokeMethod; +import static org.apache.dubbo.common.utils.MethodUtils.overrides; + public class MethodUtilsTest { @Test @@ -53,6 +64,38 @@ public class MethodUtilsTest { Assertions.assertFalse(MethodUtils.isDeprecated(MethodTestClazz.class.getMethod("getValue"))); } + @Test + public void testIsMetaMethod() { + boolean containMetaMethod = false; + for (Method method : MethodTestClazz.class.getMethods()) { + if (MethodUtils.isMetaMethod(method)) { + containMetaMethod = true; + } + } + Assertions.assertTrue(containMetaMethod); + } + + @Test + public void testGetMethods() throws NoSuchMethodException { + Assertions.assertTrue(getDeclaredMethods(MethodTestClazz.class, excludedDeclaredClass(String.class)).size() > 0); + Assertions.assertTrue(getMethods(MethodTestClazz.class).size() > 0); + Assertions.assertTrue(getAllDeclaredMethods(MethodTestClazz.class).size() > 0); + Assertions.assertTrue(getAllMethods(MethodTestClazz.class).size() > 0); + Assertions.assertNotNull(findMethod(MethodTestClazz.class, "getValue")); + + MethodTestClazz methodTestClazz = new MethodTestClazz(); + invokeMethod(methodTestClazz, "setValue", "Test"); + Assertions.assertEquals(methodTestClazz.getValue(), "Test"); + + Assertions.assertTrue(overrides(MethodOverrideClazz.class.getMethod("get"), + MethodTestClazz.class.getMethod("get"))); + Assertions.assertEquals(findNearestOverriddenMethod(MethodOverrideClazz.class.getMethod("get")), + MethodTestClazz.class.getMethod("get")); + Assertions.assertEquals(findOverriddenMethod(MethodOverrideClazz.class.getMethod("get"), MethodOverrideClazz.class), + MethodTestClazz.class.getMethod("get")); + + } + public class MethodTestClazz { private String value; @@ -64,10 +107,21 @@ public class MethodUtilsTest { this.value = value; } + public MethodTestClazz get() { + return this; + } + @Deprecated public Boolean deprecatedMethod() { return true; } } + public class MethodOverrideClazz extends MethodTestClazz { + @Override + public MethodTestClazz get() { + return this; + } + } + } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java index ea2c54e38d..b6416aa302 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java @@ -35,6 +35,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -193,11 +194,15 @@ public class NetUtilsTest { public void testIsValidV6Address() { String saved = System.getProperty("java.net.preferIPv6Addresses", "false"); System.setProperty("java.net.preferIPv6Addresses", "true"); + InetAddress address = NetUtils.getLocalAddress(); - if (address instanceof Inet6Address) { - assertThat(NetUtils.isPreferIPV6Address(), equalTo(true)); - } + boolean isPreferIPV6Address = NetUtils.isPreferIPV6Address(); + + // Restore system property to previous value before executing test System.setProperty("java.net.preferIPv6Addresses", saved); + + assumeTrue(address instanceof Inet6Address); + assertThat(isPreferIPV6Address, equalTo(true)); } /** diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java index 24cb292104..e615dd97b1 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java @@ -133,12 +133,13 @@ public class PojoUtilsTest { @Test public void test_pojo() throws Exception { assertObject(new Person()); - assertObject(new SerializablePerson()); + assertObject(new BasicTestData(false, '\0', (byte) 0, (short) 0, 0, 0L, 0F, 0D)); + assertObject(new SerializablePerson(Character.MIN_VALUE, false)); } @Test public void test_has_no_nullary_constructor_pojo() { - assertObject(new User(1,"fibbery")); + assertObject(new User(1, "fibbery")); } @Test @@ -147,7 +148,7 @@ public class PojoUtilsTest { List list = new ArrayList(); list.add(new Person()); - list.add(new SerializablePerson()); + list.add(new SerializablePerson(Character.MIN_VALUE, false)); map.put("k", list); @@ -198,6 +199,11 @@ public class PojoUtilsTest { assertArrayObject(new Float[]{37F, -39F, 123456.7F}); assertArrayObject(new Double[]{37D, -39D, 123456.7D}); + + assertObject(new int[][]{{37, -39, 12456}}); + assertObject(new Integer[][][]{{{37, -39, 12456}}}); + + assertArrayObject(new Integer[]{37, -39, 12456}); } @Test @@ -287,7 +293,7 @@ public class PojoUtilsTest { JSONObject jsonObject = new JSONObject(); jsonObject.put("1", "test"); @SuppressWarnings("unchecked") - Map value = (Map)PojoUtils.realize(jsonObject, + Map value = (Map) PojoUtils.realize(jsonObject, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); method.invoke(new PojoUtilsTest(), value); @@ -303,7 +309,7 @@ public class PojoUtilsTest { List list = new ArrayList<>(1); list.add(jsonObject); @SuppressWarnings("unchecked") - List> result = (List>)PojoUtils.realize( + List> result = (List>) PojoUtils.realize( list, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); @@ -311,9 +317,11 @@ public class PojoUtilsTest { assertEquals("test", result.get(0).get(1)); } - public void setMap(Map map) {} + public void setMap(Map map) { + } - public void setListMap(List> list) {} + public void setListMap(List> list) { + } @Test public void testException() throws Exception { @@ -756,6 +764,78 @@ public class PojoUtilsTest { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY } + public static class BasicTestData { + + public boolean a; + public char b; + public byte c; + public short d; + public int e; + public long f; + public float g; + public double h; + + public BasicTestData(boolean a, char b, byte c, short d, int e, long f, float g, double h) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.e = e; + this.f = f; + this.g = g; + this.h = h; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (a ? 1 : 2); + result = prime * result + b; + result = prime * result + c; + result = prime * result + c; + result = prime * result + e; + result = (int) (prime * result + f); + result = (int) (prime * result + g); + result = (int) (prime * result + h); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BasicTestData other = (BasicTestData) obj; + if (a != other.a) { + return false; + } + if (b != other.b) { + return false; + } + if (c != other.c) { + return false; + } + if (e != other.e) { + return false; + } + if (f != other.f) { + return false; + } + if (g != other.g) { + return false; + } + if (h != other.h) { + return false; + } + return true; + } + + } + public static class Parent { public String gender; public String email; @@ -901,4 +981,4 @@ public class PojoUtilsTest { boolean isUrgent(); } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java index 6b6b7f6615..d9adde8443 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java @@ -416,18 +416,44 @@ public class ReflectUtilsTest { Assertions.assertEquals("java.lang.String", types1[0].getTypeName()); Assertions.assertEquals("java.lang.String", types1[1].getTypeName()); - Type[] types2 = ReflectUtils.getReturnTypes(clazz.getMethod("getListFuture")); - Assertions.assertEquals("java.util.List", types2[0].getTypeName()); - Assertions.assertEquals("java.util.List", types2[1].getTypeName()); + Type[] types2 = ReflectUtils.getReturnTypes(clazz.getMethod("getT")); + Assertions.assertEquals("java.lang.String", types2[0].getTypeName()); + Assertions.assertEquals("T", types2[1].getTypeName()); + + Type[] types3 = ReflectUtils.getReturnTypes(clazz.getMethod("getS")); + Assertions.assertEquals("java.lang.Object", types3[0].getTypeName()); + Assertions.assertEquals("S", types3[1].getTypeName()); + + Type[] types4 = ReflectUtils.getReturnTypes(clazz.getMethod("getListFuture")); + Assertions.assertEquals("java.util.List", types4[0].getTypeName()); + Assertions.assertEquals("java.util.List", types4[1].getTypeName()); + + Type[] types5 = ReflectUtils.getReturnTypes(clazz.getMethod("getGenericWithUpperFuture")); + // T extends String, the first arg should be the upper bound of param + Assertions.assertEquals("java.lang.String", types5[0].getTypeName()); + Assertions.assertEquals("T", types5[1].getTypeName()); + + Type[] types6 = ReflectUtils.getReturnTypes(clazz.getMethod("getGenericFuture")); + // default upper bound is Object + Assertions.assertEquals("java.lang.Object", types6[0].getTypeName()); + Assertions.assertEquals("S", types6[1].getTypeName()); } - public interface TypeClass { + public interface TypeClass { CompletableFuture getFuture(); String getString(); + T getT(); + + S getS(); + CompletableFuture> getListFuture(); + + CompletableFuture getGenericWithUpperFuture(); + + CompletableFuture getGenericFuture(); } public static class EmptyClass { diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/external/org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt b/dubbo-common/src/test/resources/META-INF/dubbo/external/org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt new file mode 100644 index 0000000000..be83ff5e71 --- /dev/null +++ b/dubbo-common/src/test/resources/META-INF/dubbo/external/org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt @@ -0,0 +1 @@ +duplicated=org.apache.dubbo.common.extension.duplicated.impl.DuplicatedOverriddenExt2 \ No newline at end of file diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/external/org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt b/dubbo-common/src/test/resources/META-INF/dubbo/external/org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt new file mode 100644 index 0000000000..237c8fdbf4 --- /dev/null +++ b/dubbo-common/src/test/resources/META-INF/dubbo/external/org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt @@ -0,0 +1 @@ +duplicated=org.apache.dubbo.common.extension.duplicated.impl.DuplicatedWithoutOverriddenExt2 \ No newline at end of file diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt new file mode 100644 index 0000000000..a7a66efb8f --- /dev/null +++ b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.duplicated.DuplicatedOverriddenExt @@ -0,0 +1 @@ +duplicated=org.apache.dubbo.common.extension.duplicated.impl.DuplicatedOverriddenExt1 \ No newline at end of file diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt new file mode 100644 index 0000000000..e655ee69e1 --- /dev/null +++ b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.duplicated.DuplicatedWithoutOverriddenExt @@ -0,0 +1 @@ +duplicated=org.apache.dubbo.common.extension.duplicated.impl.DuplicatedWithoutOverriddenExt1 \ No newline at end of file diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.wrapper.Demo b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.wrapper.Demo new file mode 100644 index 0000000000..a346d659f8 --- /dev/null +++ b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.wrapper.Demo @@ -0,0 +1,4 @@ +demo=org.apache.dubbo.common.extension.wrapper.impl.DemoImpl +wrapper=org.apache.dubbo.common.extension.wrapper.impl.DemoWrapper +wrapper2=org.apache.dubbo.common.extension.wrapper.impl.DemoWrapper2 +demo2=org.apache.dubbo.common.extension.wrapper.impl.DemoImpl \ No newline at end of file diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java index 21f4675e78..4182ea65c5 100644 --- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java +++ b/dubbo-compatible/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java @@ -51,10 +51,6 @@ public abstract class FailbackRegistry implements org.apache.dubbo.registry.Regi failbackRegistry.removeFailedUnsubscribedTask(url.getOriginalURL(), new NotifyListener.ReverseCompatibleNotifyListener(listener)); } - public void removeFailedNotifiedTask(URL url, NotifyListener listener) { - failbackRegistry.removeFailedNotifiedTask(url.getOriginalURL(), new NotifyListener.ReverseCompatibleNotifyListener(listener)); - } - @Override public void register(URL url) { failbackRegistry.register(url.getOriginalURL()); diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java index 5b9711e6c5..b2bba70b7f 100644 --- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java +++ b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java @@ -45,15 +45,28 @@ public class FutureAdapter implements Future { this.future = future; } + public FutureAdapter(ResponseFuture responseFuture) { + this.future = new CompletableFuture<>(); + responseFuture.setCallback(new ResponseCallback() { + @Override + public void done(Object response) { + future.complete(response); + } + + @Override + public void caught(Throwable exception) { + future.completeExceptionally(exception); + } + }); + } + public ResponseFuture getFuture() { return new ResponseFuture() { @Override public Object get() throws RemotingException { try { return future.get(); - } catch (InterruptedException e) { - throw new RemotingException(e); - } catch (ExecutionException e) { + } catch (InterruptedException | ExecutionException e) { throw new RemotingException(e); } } @@ -62,11 +75,7 @@ public class FutureAdapter implements Future { public Object get(int timeoutInMillis) throws RemotingException { try { return future.get(timeoutInMillis, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - throw new RemotingException(e); - } catch (ExecutionException e) { - throw new RemotingException(e); - } catch (TimeoutException e) { + } catch (InterruptedException | TimeoutException | ExecutionException e) { throw new RemotingException(e); } } diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/echo/EchoServiceTest.java b/dubbo-compatible/src/test/java/org/apache/dubbo/echo/EchoServiceTest.java index 24cbfa291a..b650b40bda 100644 --- a/dubbo-compatible/src/test/java/org/apache/dubbo/echo/EchoServiceTest.java +++ b/dubbo-compatible/src/test/java/org/apache/dubbo/echo/EchoServiceTest.java @@ -23,11 +23,10 @@ import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProxyFactory; +import org.apache.dubbo.rpc.service.EchoService; import org.apache.dubbo.service.DemoService; import org.apache.dubbo.service.DemoServiceImpl; -import com.alibaba.dubbo.rpc.service.EchoService; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; 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 fba3c40d3a..d34121c2e8 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 @@ -33,6 +33,7 @@ import org.apache.dubbo.service.ComplexObject; import org.apache.dubbo.service.DemoService; import org.apache.dubbo.service.DemoServiceImpl; +import com.alibaba.dubbo.config.ReferenceConfig; import com.alibaba.fastjson.JSON; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -88,6 +89,29 @@ public class GenericServiceTest { exporter.unexport(); } + @Test + public void testGenericCompatible() { + DemoService server = new DemoServiceImpl(); + ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); + Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); + URL url = URL.valueOf("dubbo://127.0.0.1:5342/" + DemoService.class.getName() + "?version=1.0.0&generic=true$timeout=3000"); + Exporter exporter = protocol.export(proxyFactory.getInvoker(server, DemoService.class, url)); + + // simulate normal invoke + ReferenceConfig oldReferenceConfig = new ReferenceConfig<>(); + oldReferenceConfig.setGeneric(true); + oldReferenceConfig.setInterface(DemoService.class.getName()); + oldReferenceConfig.checkAndUpdateSubConfigs(); + Invoker invoker = protocol.refer(oldReferenceConfig.getInterfaceClass(), url); + com.alibaba.dubbo.rpc.service.GenericService client = (com.alibaba.dubbo.rpc.service.GenericService) proxyFactory.getProxy(invoker, true); + + Object result = client.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"haha"}); + Assertions.assertEquals("hello haha", result); + + invoker.destroy(); + exporter.unexport(); + } + @Test public void testGenericComplexCompute4FullServiceMetadata() { DemoService server = new DemoServiceImpl(); diff --git a/dubbo-config/dubbo-config-api/pom.xml b/dubbo-config/dubbo-config-api/pom.xml index 86880ebbf1..501d3653ee 100644 --- a/dubbo-config/dubbo-config-api/pom.xml +++ b/dubbo-config/dubbo-config-api/pom.xml @@ -160,5 +160,11 @@ test + + org.apache.curator + curator-test + test + + diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java index 084a1ab78d..a12ef09da3 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java @@ -300,7 +300,8 @@ public class ReferenceConfig extends ReferenceConfigBase { if (StringUtils.isEmpty(hostToRegistry)) { hostToRegistry = NetUtils.getLocalHost(); } else if (isInvalidLocalHost(hostToRegistry)) { - throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry); + throw new IllegalArgumentException( + "Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry); } map.put(REGISTER_IP_KEY, hostToRegistry); @@ -364,7 +365,10 @@ public class ReferenceConfig extends ReferenceConfigBase { } } if (urls.isEmpty()) { - throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config to your spring config."); + throw new IllegalStateException( + "No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + + " use dubbo version " + Version.getVersion() + + ", please config to your spring config."); } } } @@ -375,11 +379,33 @@ public class ReferenceConfig extends ReferenceConfigBase { List> invokers = new ArrayList>(); URL registryURL = null; for (URL url : urls) { - invokers.add(REF_PROTOCOL.refer(interfaceClass, url)); + Invoker referInvoker = REF_PROTOCOL.refer(interfaceClass, url); + if (shouldCheck()) { + if (referInvoker.isAvailable()) { + invokers.add(referInvoker); + } else { + referInvoker.destroy(); + } + } else { + invokers.add(referInvoker); + } + if (UrlUtils.isRegistry(url)) { registryURL = url; // use last registry url } } + + if (shouldCheck() && invokers.size() == 0) { + throw new IllegalStateException("Failed to check the status of the service " + + interfaceName + + ". No provider available for the service " + + (group == null ? "" : group + "/") + + interfaceName + + (version == null ? "" : ":" + version) + + " from the multi registry cluster" + + " use dubbo version " + Version.getVersion()); + } + if (registryURL != null) { // registry url is available // for multi-subscription scenario, use 'zone-aware' policy by default String cluster = registryURL.getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME); @@ -387,7 +413,9 @@ public class ReferenceConfig extends ReferenceConfigBase { invoker = Cluster.getCluster(cluster, false).join(new StaticDirectory(registryURL, invokers)); } else { // not a registry url, must be direct invoke. String cluster = CollectionUtils.isNotEmpty(invokers) - ? (invokers.get(0).getUrl() != null ? invokers.get(0).getUrl().getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME) : Cluster.DEFAULT) + ? + (invokers.get(0).getUrl() != null ? invokers.get(0).getUrl().getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME) : + Cluster.DEFAULT) : Cluster.DEFAULT; invoker = Cluster.getCluster(cluster).join(new StaticDirectory(invokers)); } @@ -430,11 +458,6 @@ public class ReferenceConfig extends ReferenceConfigBase { throw new IllegalStateException(" interface not allow null!"); } completeCompoundConfigs(consumer); - if (consumer != null) { - if (StringUtils.isEmpty(registryIds)) { - setRegistryIds(consumer.getRegistryIds()); - } - } // get consumer's global configuration checkDefault(); @@ -459,12 +482,8 @@ public class ReferenceConfig extends ReferenceConfigBase { checkInterfaceAndMethods(interfaceClass, getMethods()); } - //init serivceMetadata - serviceMetadata.setVersion(getVersion()); - serviceMetadata.setGroup(getGroup()); - serviceMetadata.setDefaultGroup(getGroup()); + initServiceMetadata(consumer); serviceMetadata.setServiceType(getActualInterface()); - serviceMetadata.setServiceInterfaceName(interfaceName); // TODO, uncomment this line once service key is unified serviceMetadata.setServiceKey(URL.buildKey(interfaceName, group, version)); diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java index c1d7052fc3..78a9f7010d 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java @@ -197,13 +197,10 @@ public class ServiceConfig extends ServiceConfigBase { checkAndUpdateSubConfigs(); - //init serviceMetadata - serviceMetadata.setVersion(getVersion()); - serviceMetadata.setGroup(getGroup()); - serviceMetadata.setDefaultGroup(getGroup()); + initServiceMetadata(provider); serviceMetadata.setServiceType(getInterfaceClass()); - serviceMetadata.setServiceInterfaceName(getInterface()); serviceMetadata.setTarget(getRef()); + serviceMetadata.generateServiceKey(); if (!shouldExport()) { return; @@ -334,8 +331,6 @@ public class ServiceConfig extends ServiceConfigBase { .orElse(path), group, version); // In case user specified path, register service one more time to map it to path. repository.registerService(pathKey, interfaceClass); - // TODO, uncomment this line once service key is unified - serviceMetadata.setServiceKey(pathKey); doExportUrlsFor1Protocol(protocolConfig, registryURLs); } } diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java index e5544e48bc..ea9d8f8375 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java @@ -23,7 +23,6 @@ import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory; import org.apache.dubbo.common.config.configcenter.wrapper.CompositeDynamicConfiguration; import org.apache.dubbo.common.extension.ExtensionLoader; -import org.apache.dubbo.common.lang.ShutdownHookCallback; import org.apache.dubbo.common.lang.ShutdownHookCallbacks; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; @@ -58,9 +57,6 @@ import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.metadata.ConfigurableMetadataServiceExporter; import org.apache.dubbo.config.utils.ConfigValidationUtils; import org.apache.dubbo.config.utils.ReferenceConfigCache; -import org.apache.dubbo.event.EventDispatcher; -import org.apache.dubbo.event.EventListener; -import org.apache.dubbo.event.GenericEventListener; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.metadata.MetadataServiceExporter; import org.apache.dubbo.metadata.WritableMetadataService; @@ -94,7 +90,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; import static java.lang.String.format; -import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; import static java.util.concurrent.Executors.newSingleThreadExecutor; import static org.apache.dubbo.common.config.ConfigurationUtils.parseProperties; import static org.apache.dubbo.common.config.configcenter.DynamicConfiguration.getDynamicConfiguration; @@ -123,7 +119,7 @@ import static org.apache.dubbo.remoting.Constants.CLIENT_KEY; * * @since 2.7.5 */ -public class DubboBootstrap extends GenericEventListener { +public class DubboBootstrap { public static final String DEFAULT_REGISTRY_ID = "REGISTRY#DEFAULT"; @@ -153,8 +149,6 @@ public class DubboBootstrap extends GenericEventListener { private final ExecutorService executorService = newSingleThreadExecutor(); - private final EventDispatcher eventDispatcher = EventDispatcher.getDefaultExtension(); - private final ExecutorRepository executorRepository = getExtensionLoader(ExecutorRepository.class).getDefaultExtension(); private final ConfigManager configManager; @@ -225,12 +219,7 @@ public class DubboBootstrap extends GenericEventListener { environment = ApplicationModel.getEnvironment(); DubboShutdownHook.getDubboShutdownHook().register(); - ShutdownHookCallbacks.INSTANCE.addCallback(new ShutdownHookCallback() { - @Override - public void callback() throws Throwable { - DubboBootstrap.this.destroy(); - } - }); + ShutdownHookCallbacks.INSTANCE.addCallback(DubboBootstrap.this::destroy); } public void unRegisterShutdownHook() { @@ -366,7 +355,7 @@ public class DubboBootstrap extends GenericEventListener { } public DubboBootstrap protocol(ProtocolConfig protocolConfig) { - return protocols(asList(protocolConfig)); + return protocols(singletonList(protocolConfig)); } public DubboBootstrap protocols(List protocolConfigs) { @@ -438,7 +427,7 @@ public class DubboBootstrap extends GenericEventListener { } public DubboBootstrap provider(ProviderConfig providerConfig) { - return providers(asList(providerConfig)); + return providers(singletonList(providerConfig)); } public DubboBootstrap providers(List providerConfigs) { @@ -462,7 +451,7 @@ public class DubboBootstrap extends GenericEventListener { } public DubboBootstrap consumer(ConsumerConfig consumerConfig) { - return consumers(asList(consumerConfig)); + return consumers(singletonList(consumerConfig)); } public DubboBootstrap consumers(List consumerConfigs) { @@ -476,7 +465,7 @@ public class DubboBootstrap extends GenericEventListener { // {@link ConfigCenterConfig} correlative methods public DubboBootstrap configCenter(ConfigCenterConfig configCenterConfig) { - return configCenters(asList(configCenterConfig)); + return configCenters(singletonList(configCenterConfig)); } public DubboBootstrap configCenters(List configCenterConfigs) { @@ -551,8 +540,6 @@ public class DubboBootstrap extends GenericEventListener { initMetadataService(); - initEventListener(); - if (logger.isInfoEnabled()) { logger.info(NAME + " has been initialized!"); } @@ -715,7 +702,9 @@ public class DubboBootstrap extends GenericEventListener { cc.getParameters().put(CLIENT_KEY, registryConfig.getClient()); cc.setProtocol(protocol); cc.setPort(port); - cc.setGroup(registryConfig.getGroup()); + if (StringUtils.isNotEmpty(registryConfig.getGroup())) { + cc.setGroup(registryConfig.getGroup()); + } cc.setAddress(getRegistryCompatibleAddress(registryConfig)); cc.setNamespace(registryConfig.getGroup()); cc.setUsername(registryConfig.getUsername()); @@ -886,14 +875,6 @@ public class DubboBootstrap extends GenericEventListener { this.metadataServiceExporter = new ConfigurableMetadataServiceExporter(metadataService); } - /** - * Initialize {@link EventListener} - */ - private void initEventListener() { - // Add current instance into listeners - addEventListener(this); - } - /** * Start the bootstrap */ @@ -927,12 +908,16 @@ public class DubboBootstrap extends GenericEventListener { if (logger.isInfoEnabled()) { logger.info(NAME + " is ready."); } + ExtensionLoader exts = getExtensionLoader(DubboBootstrapStartStopListener.class); + exts.getSupportedExtensionInstances().forEach(ext -> ext.onStart(this)); }).start(); } else { startup.set(true); if (logger.isInfoEnabled()) { logger.info(NAME + " is ready."); } + ExtensionLoader exts = getExtensionLoader(DubboBootstrapStartStopListener.class); + exts.getSupportedExtensionInstances().forEach(ext -> ext.onStart(this)); } if (logger.isInfoEnabled()) { logger.info(NAME + " has started."); @@ -1002,6 +987,7 @@ public class DubboBootstrap extends GenericEventListener { return shutdown.get(); } + public DubboBootstrap stop() throws IllegalStateException { destroy(); return this; @@ -1065,17 +1051,6 @@ public class DubboBootstrap extends GenericEventListener { return null; } - /** - * Add an instance of {@link EventListener} - * - * @param listener {@link EventListener} - * @return {@link DubboBootstrap} - */ - public DubboBootstrap addEventListener(EventListener listener) { - eventDispatcher.addEventListener(listener); - return this; - } - /** * export {@link MetadataService} */ @@ -1098,8 +1073,12 @@ public class DubboBootstrap extends GenericEventListener { if (exportAsync) { ExecutorService executor = executorRepository.getServiceExporterExecutor(); Future future = executor.submit(() -> { - sc.export(); - exportedServices.add(sc); + try { + sc.export(); + exportedServices.add(sc); + }catch (Throwable t) { + logger.error("export async catch error : " + t.getMessage(), t); + } }); asyncExportingFutures.add(future); } else { @@ -1244,12 +1223,14 @@ public class DubboBootstrap extends GenericEventListener { unreferServices(); destroyRegistries(); - DubboShutdownHook.destroyProtocols(); + destroyServiceDiscoveries(); clear(); shutdown(); release(); + ExtensionLoader exts = getExtensionLoader(DubboBootstrapStartStopListener.class); + exts.getSupportedExtensionInstances().forEach(ext -> ext.onStop(this)); } } finally { destroyLock.unlock(); diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrapStartStopListener.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrapStartStopListener.java new file mode 100644 index 0000000000..dc7818d72c --- /dev/null +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrapStartStopListener.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.config.bootstrap; + +import org.apache.dubbo.common.extension.SPI; + +/** + * call on DubboBootstrap start or stop. + * + * @scene 2.7.9 + * @see DubboBootstrap + */ +@SPI +public interface DubboBootstrapStartStopListener { + + void onStart(DubboBootstrap bootstrap); + + void onStop(DubboBootstrap bootstrap); +} diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ReferenceBuilder.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ReferenceBuilder.java index 99af153c30..9608b7f1bb 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ReferenceBuilder.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ReferenceBuilder.java @@ -31,7 +31,7 @@ import static org.apache.dubbo.common.utils.StringUtils.toCommaDelimitedString; * * @since 2.7 */ -public class ReferenceBuilder extends AbstractReferenceBuilder> { +public class ReferenceBuilder extends AbstractReferenceBuilder, ReferenceBuilder> { /** * The interface name of the reference service */ @@ -74,8 +74,8 @@ public class ReferenceBuilder extends AbstractReferenceBuilder ReferenceBuilder newBuilder() { + return new ReferenceBuilder<>(); } public ReferenceBuilder id(String id) { diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ServiceBuilder.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ServiceBuilder.java index cfea5806b9..b02e7c1d55 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ServiceBuilder.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ServiceBuilder.java @@ -29,7 +29,7 @@ import java.util.List; * * @since 2.7 */ -public class ServiceBuilder extends AbstractServiceBuilder> { +public class ServiceBuilder extends AbstractServiceBuilder, ServiceBuilder> { /** * The interface name of the exported service */ @@ -69,11 +69,11 @@ public class ServiceBuilder extends AbstractServiceBuilder ServiceBuilder newBuilder() { + return new ServiceBuilder<>(); } - public ServiceBuilder id(String id) { + public ServiceBuilder id(String id) { return super.id(id); } diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java index 3422809b00..2e3f5e6471 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java @@ -50,7 +50,7 @@ import org.apache.dubbo.config.SslConfig; import org.apache.dubbo.monitor.MonitorFactory; import org.apache.dubbo.monitor.MonitorService; import org.apache.dubbo.registry.RegistryService; -import org.apache.dubbo.remoting.Codec; +import org.apache.dubbo.remoting.Codec2; import org.apache.dubbo.remoting.Dispatcher; import org.apache.dubbo.remoting.Transporter; import org.apache.dubbo.remoting.exchange.Exchanger; @@ -63,7 +63,10 @@ import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.support.MockInvoker; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -104,6 +107,7 @@ import static org.apache.dubbo.config.Constants.ARCHITECTURE; import static org.apache.dubbo.config.Constants.CONTEXTPATH_KEY; import static org.apache.dubbo.config.Constants.DUBBO_IP_TO_REGISTRY; import static org.apache.dubbo.config.Constants.ENVIRONMENT; +import static org.apache.dubbo.config.Constants.IGNORE_CHECK_KEYS; import static org.apache.dubbo.config.Constants.LAYER_KEY; import static org.apache.dubbo.config.Constants.NAME; import static org.apache.dubbo.config.Constants.ORGANIZATION; @@ -173,6 +177,10 @@ public class ConfigValidationUtils { */ private static final Pattern PATTERN_KEY = Pattern.compile("[*,\\-._0-9a-zA-Z]+"); + public static final String IPV6_START_MARK = "["; + + public static final String IPV6_END_MARK = "]"; + public static List loadRegistries(AbstractInterfaceConfig interfaceConfig, boolean provider) { // check && override if necessary List registryList = new ArrayList(); @@ -503,12 +511,12 @@ public class ConfigValidationUtils { if (config != null) { String name = config.getName(); checkName("name", name); - checkName(HOST_KEY, config.getHost()); + checkHost(HOST_KEY, config.getHost()); checkPathName("contextpath", config.getContextpath()); if (DUBBO_PROTOCOL.equals(name)) { - checkMultiExtension(Codec.class, CODEC_KEY, config.getCodec()); + checkMultiExtension(Codec2.class, CODEC_KEY, config.getCodec()); checkMultiExtension(Serialization.class, SERIALIZATION_KEY, config.getSerialization()); checkMultiExtension(Transporter.class, SERVER_KEY, config.getServer()); checkMultiExtension(Transporter.class, CLIENT_KEY, config.getClient()); @@ -622,6 +630,22 @@ public class ConfigValidationUtils { checkProperty(property, value, MAX_LENGTH, PATTERN_NAME); } + public static void checkHost(String property, String value) { + if (StringUtils.isEmpty(value)) { + return; + } + if (value.startsWith(IPV6_START_MARK) && value.endsWith(IPV6_END_MARK)) { + // if the value start with "[" and end with "]", check whether it is IPV6 + try { + InetAddress.getByName(value); + return; + } catch (UnknownHostException e) { + // not a IPv6 string, do nothing, go on to checkName + } + } + checkName(property, value); + } + public static void checkNameHasSymbol(String property, String value) { checkProperty(property, value, MAX_LENGTH, PATTERN_NAME_HAS_SYMBOL); } @@ -646,8 +670,14 @@ public class ConfigValidationUtils { if (CollectionUtils.isEmptyMap(parameters)) { return; } + List ignoreCheckKeys = new ArrayList<>(); + ignoreCheckKeys.add(BACKUP_KEY); + String ignoreCheckKeysStr = parameters.get(IGNORE_CHECK_KEYS); + if (!StringUtils.isBlank(ignoreCheckKeysStr)) { + ignoreCheckKeys.addAll(Arrays.asList(ignoreCheckKeysStr.split(","))); + } for (Map.Entry entry : parameters.entrySet()) { - if (!entry.getKey().equals(BACKUP_KEY)) { + if (!ignoreCheckKeys.contains(entry.getKey())) { checkNameHasSymbol(entry.getKey(), entry.getValue()); } } diff --git a/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener b/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener index 0f4c8fea4a..3d90c3b90a 100644 --- a/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener +++ b/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener @@ -1 +1 @@ -config-logging=org.apache.dubbo.config.event.listener.LoggingEventListener \ No newline at end of file +config-logging=org.apache.dubbo.config.event.listener.LoggingEventListener diff --git a/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataServiceExporter b/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataServiceExporter index 1b843b6bbc..2bda3f23b3 100644 --- a/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataServiceExporter +++ b/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataServiceExporter @@ -1,3 +1,2 @@ # since 2.7.8 local = org.apache.dubbo.config.metadata.ConfigurableMetadataServiceExporter -remote = org.apache.dubbo.config.metadata.RemoteMetadataServiceExporter \ No newline at end of file diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java index 04d5f7259d..d81f51c020 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java @@ -106,6 +106,15 @@ public class AbstractConfigTest { } }*/ + @Test + public void testValidateProtocolConfig() { + ProtocolConfig protocolConfig = new ProtocolConfig(); + protocolConfig.setCodec("exchange"); + protocolConfig.setName("test"); + protocolConfig.setHost("host"); + ConfigValidationUtils.validateProtocolConfig(protocolConfig); + } + @Test public void testAppendParameters1() throws Exception { Map parameters = new HashMap(); @@ -170,12 +179,14 @@ public class AbstractConfigTest { @Test public void checkMultiExtension1() throws Exception { - Assertions.assertThrows(IllegalStateException.class, () -> ConfigValidationUtils.checkMultiExtension(Greeting.class, "hello", "default,world")); + Assertions.assertThrows(IllegalStateException.class, + () -> ConfigValidationUtils.checkMultiExtension(Greeting.class, "hello", "default,world")); } @Test public void checkMultiExtension2() throws Exception { - Assertions.assertThrows(IllegalStateException.class, () -> ConfigValidationUtils.checkMultiExtension(Greeting.class, "hello", "default,-world")); + Assertions.assertThrows(IllegalStateException.class, + () -> ConfigValidationUtils.checkMultiExtension(Greeting.class, "hello", "default,-world")); } @Test diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MetadataReportConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MetadataReportConfigTest.java new file mode 100644 index 0000000000..463f3830cd --- /dev/null +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MetadataReportConfigTest.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.config; + +import org.apache.dubbo.common.URL; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +public class MetadataReportConfigTest { + @Test + public void testFile() { + MetadataReportConfig metadataReportConfig = new MetadataReportConfig(); + metadataReportConfig.setFile("file"); + assertThat(metadataReportConfig.getFile(), equalTo("file")); + + metadataReportConfig.setAddress("127.0.0.1:2181"); + URL url = metadataReportConfig.toUrl(); + assertThat(url.getParameter("file"), equalTo("file")); + + } + +} diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MethodConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MethodConfigTest.java index c064f95605..6b853d2e84 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MethodConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MethodConfigTest.java @@ -87,12 +87,12 @@ public class MethodConfigTest { assertThat(EXECUTES, equalTo(methodConfig.getExecutes().intValue())); assertThat(DEPERECATED, equalTo(methodConfig.getDeprecated())); assertThat(STICKY, equalTo(methodConfig.getSticky())); -// assertThat(ONINVOKE, equalTo(methodConfig.getOninvoke())); -// assertThat(ONINVOKE_METHOD, equalTo(methodConfig.getOninvokeMethod())); -// assertThat(ONTHROW, equalTo(methodConfig.getOnthrow())); -// assertThat(ONTHROW_METHOD, equalTo(methodConfig.getOnthrowMethod())); -// assertThat(ONRETURN, equalTo(methodConfig.getOnreturn())); -// assertThat(ONRETURN_METHOD, equalTo(methodConfig.getOnreturnMethod())); + assertThat(ONINVOKE, equalTo(methodConfig.getOninvoke())); + assertThat(ONINVOKE_METHOD, equalTo(methodConfig.getOninvokeMethod())); + assertThat(ONTHROW, equalTo(methodConfig.getOnthrow())); + assertThat(ONTHROW_METHOD, equalTo(methodConfig.getOnthrowMethod())); + assertThat(ONRETURN, equalTo(methodConfig.getOnreturn())); + assertThat(ONRETURN_METHOD, equalTo(methodConfig.getOnreturnMethod())); assertThat(CACHE, equalTo(methodConfig.getCache())); assertThat(VALIDATION, equalTo(methodConfig.getValidation())); assertThat(ARGUMENTS_INDEX, equalTo(methodConfig.getArguments().get(0).getIndex().intValue())); diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java index 3e8ad3acdb..0dbf17f30e 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java @@ -16,44 +16,58 @@ */ package org.apache.dubbo.config; +import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.config.annotation.Argument; import org.apache.dubbo.config.annotation.Method; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.api.DemoService; +import org.apache.dubbo.config.bootstrap.DubboBootstrap; import org.apache.dubbo.config.provider.impl.DemoServiceImpl; import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.curator.test.TestingServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import java.io.IOException; + import static org.apache.dubbo.rpc.Constants.LOCAL_PROTOCOL; import static org.apache.dubbo.rpc.Constants.SCOPE_REMOTE; public class ReferenceConfigTest { + private TestingServer zkServer; + private String registryUrl; @BeforeEach - public void setUp() { + public void setUp() throws Exception { ApplicationModel.reset(); + int zkServerPort = NetUtils.getAvailablePort(NetUtils.getRandomPort()); + this.zkServer = new TestingServer(zkServerPort, true); + this.zkServer.start(); + this.registryUrl = "zookeeper://localhost:" + zkServerPort; } @AfterEach - public void tearDown() { + public void tearDown() throws IOException { + zkServer.stop(); ApplicationModel.reset(); } @Test + @Disabled("Disabled due to Github Actions environment") public void testInjvm() throws Exception { ApplicationConfig application = new ApplicationConfig(); application.setName("test-protocol-random-port"); ApplicationModel.getConfigManager().setApplication(application); RegistryConfig registry = new RegistryConfig(); - registry.setAddress("multicast://224.5.6.7:1234"); + registry.setAddress(registryUrl); ProtocolConfig protocol = new ProtocolConfig(); - protocol.setName("mockprotocol"); + protocol.setName("dubbo"); ServiceConfig demoService; demoService = new ServiceConfig(); @@ -75,8 +89,12 @@ public class ReferenceConfigTest { rc.getInvoker().getUrl().getProtocol())); } finally { System.clearProperty("java.net.preferIPv4Stack"); + rc.destroy(); demoService.unexport(); } + + // Manually trigger dubbo resource recycling. + DubboBootstrap.getInstance().destroy(); } /** @@ -89,7 +107,7 @@ public class ReferenceConfigTest { ApplicationModel.getConfigManager().setApplication(application); RegistryConfig registry = new RegistryConfig(); - registry.setAddress("multicast://224.5.6.7:1234"); + registry.setAddress(registryUrl); ProtocolConfig protocol = new ProtocolConfig(); protocol.setName("mockprotocol"); @@ -122,6 +140,8 @@ public class ReferenceConfigTest { } catch (Exception e) { e.printStackTrace(); } finally { + rc.destroy(); + sc.unexport(); System.clearProperty("java.net.preferIPv4Stack"); } Assertions.assertTrue(success); @@ -141,15 +161,15 @@ public class ReferenceConfigTest { Assertions.assertEquals(3, (int) ((MethodConfig) referenceConfig.getMethods().get(0)).getActives()); Assertions.assertEquals(5, (int) ((MethodConfig) referenceConfig.getMethods().get(0)).getExecutes()); Assertions.assertTrue(((MethodConfig) referenceConfig.getMethods().get(0)).isAsync()); - Assertions.assertEquals(((MethodConfig) referenceConfig.getMethods().get(0)).getOninvoke(), "i"); - Assertions.assertEquals(((MethodConfig) referenceConfig.getMethods().get(0)).getOnreturn(), "r"); - Assertions.assertEquals(((MethodConfig) referenceConfig.getMethods().get(0)).getOnthrow(), "t"); + Assertions.assertEquals(((MethodConfig) referenceConfig.getMethods().get(0)).getOninvokeMethod(), "i"); + Assertions.assertEquals(((MethodConfig) referenceConfig.getMethods().get(0)).getOnreturnMethod(), "r"); + Assertions.assertEquals(((MethodConfig) referenceConfig.getMethods().get(0)).getOnthrowMethod(), "t"); Assertions.assertEquals(((MethodConfig) referenceConfig.getMethods().get(0)).getCache(), "c"); } @Reference(methods = {@Method(name = "sayHello", timeout = 1300, retries = 4, loadbalance = "random", async = true, - actives = 3, executes = 5, deprecated = true, sticky = true, oninvoke = "i", onthrow = "t", onreturn = "r", cache = "c", validation = "v", + actives = 3, executes = 5, deprecated = true, sticky = true, oninvoke = "instance.i", onthrow = "instance.t", onreturn = "instance.r", cache = "c", validation = "v", arguments = {@Argument(index = 24, callback = true, type = "sss")})}) private InnerTest innerTest; diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/RegistryConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/RegistryConfigTest.java index fb74b1ea48..4a6303711a 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/RegistryConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/RegistryConfigTest.java @@ -17,6 +17,9 @@ package org.apache.dubbo.config; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.UrlUtils; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -24,6 +27,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import static org.apache.dubbo.common.constants.CommonConstants.PREFERRED_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY; import static org.apache.dubbo.config.Constants.SHUTDOWN_TIMEOUT_KEY; import static org.hamcrest.CoreMatchers.is; @@ -187,4 +191,28 @@ public class RegistryConfigTest { Assertions.assertNotEquals(registry1, registry2); } + @Test + public void testPreferredWithTrueValue() { + RegistryConfig registry = new RegistryConfig(); + registry.setPreferred(true); + Map map = new HashMap<>(); + // process Parameter annotation + AbstractConfig.appendParameters(map, registry); + // Simulate the check that ZoneAwareClusterInvoker#doInvoke do + URL url = UrlUtils.parseURL("zookeeper://127.0.0.1:2181", map); + Assertions.assertTrue(url.getParameter(PREFERRED_KEY, false)); + } + + @Test + public void testPreferredWithFalseValue() { + RegistryConfig registry = new RegistryConfig(); + registry.setPreferred(false); + Map map = new HashMap<>(); + // Process Parameter annotation + AbstractConfig.appendParameters(map, registry); + // Simulate the check that ZoneAwareClusterInvoker#doInvoke do + URL url = UrlUtils.parseURL("zookeeper://127.0.0.1:2181", map); + Assertions.assertFalse(url.getParameter(PREFERRED_KEY, false)); + } + } diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java index ed463829d0..72f39ae188 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java @@ -45,11 +45,13 @@ import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_BEAN; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_DEFAULT; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_NATIVE_JAVA; +import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.config.Constants.SHUTDOWN_TIMEOUT_KEY; import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY; import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY; @@ -154,12 +156,30 @@ public class ServiceConfigTest { Mockito.verify(protocolDelegate).export(Mockito.any(Invoker.class)); } + @Test + public void testVersionAndGroupConfigFromProvider() { + //Service no configuration version , the Provider configured. + service.getProvider().setVersion("1.0.0"); + service.getProvider().setGroup("groupA"); + service.export(); + + String serviceVersion = service.getVersion(); + String serviceVersion2 = service.toUrl().getParameter(VERSION_KEY); + + String group = service.getGroup(); + String group2 = service.toUrl().getParameter(GROUP_KEY); + + assertEquals(serviceVersion2, serviceVersion); + assertEquals(group, group2); + } + @Test public void testProxy() throws Exception { service2.export(); assertThat(service2.getExportedUrls(), hasSize(1)); assertEquals(2, TestProxyFactory.count); // local injvm and registry protocol, so expected is 2 + TestProxyFactory.count = 0; } diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java index ad571f86cd..0d6209fc59 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java @@ -51,6 +51,7 @@ public class PublishingServiceDefinitionListenerTest { @BeforeEach public void init() { + ApplicationModel.reset(); String metadataType = DEFAULT_METADATA_STORAGE_TYPE; ConfigManager configManager = ApplicationModel.getConfigManager(); ApplicationConfig applicationConfig = new ApplicationConfig("dubbo-demo-provider"); diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigDefaultPropertyValueBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigDefaultPropertyValueBeanPostProcessor.java index ec99c1c8ea..8b75859ace 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigDefaultPropertyValueBeanPostProcessor.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigDefaultPropertyValueBeanPostProcessor.java @@ -16,7 +16,9 @@ */ package org.apache.dubbo.config.spring.beans.factory.config; +import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.AbstractConfig; +import org.apache.dubbo.config.ProtocolConfig; import com.alibaba.spring.beans.factory.config.GenericBeanPostProcessorAdapter; import org.springframework.beans.BeansException; @@ -54,7 +56,14 @@ public class DubboConfigDefaultPropertyValueBeanPostProcessor extends GenericBea protected void processBeforeInitialization(AbstractConfig dubboConfigBean, String beanName) throws BeansException { // [Feature] https://github.com/apache/dubbo/issues/5721 setBeanNameAsDefaultValue(dubboConfigBean, "id", beanName); - setBeanNameAsDefaultValue(dubboConfigBean, "name", beanName); + if (dubboConfigBean instanceof ProtocolConfig) { + ProtocolConfig config = (ProtocolConfig) dubboConfigBean; + if (StringUtils.isEmpty(config.getName())) { + config.setName("dubbo"); + } + } else { + setBeanNameAsDefaultValue(dubboConfigBean, "name", beanName); + } } @Override diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java index 4a988804bf..1911eb50e3 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java @@ -16,8 +16,11 @@ */ package org.apache.dubbo.config.spring.context; +import org.apache.dubbo.config.DubboShutdownHook; import org.apache.dubbo.config.bootstrap.DubboBootstrap; +import com.alibaba.spring.context.OnceApplicationContextEventListener; +import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ApplicationContextEvent; import org.springframework.context.event.ContextClosedEvent; @@ -30,8 +33,7 @@ import org.springframework.core.Ordered; * * @since 2.7.5 */ -public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener - implements Ordered { +public class DubboBootstrapApplicationListener extends OnceApplicationContextEventListener implements Ordered { /** * The bean name of {@link DubboBootstrapApplicationListener} @@ -46,8 +48,17 @@ public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicati this.dubboBootstrap = DubboBootstrap.getInstance(); } + public DubboBootstrapApplicationListener(ApplicationContext applicationContext) { + super(applicationContext); + this.dubboBootstrap = DubboBootstrap.getInstance(); + DubboBootstrapStartStopListenerSpringAdapter.applicationContext = applicationContext; + } + @Override public void onApplicationContextEvent(ApplicationContextEvent event) { + if (DubboBootstrapStartStopListenerSpringAdapter.applicationContext == null) { + DubboBootstrapStartStopListenerSpringAdapter.applicationContext = event.getApplicationContext(); + } if (event instanceof ContextRefreshedEvent) { onContextRefreshedEvent((ContextRefreshedEvent) event); } else if (event instanceof ContextClosedEvent) { @@ -60,7 +71,7 @@ public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicati } private void onContextClosedEvent(ContextClosedEvent event) { - dubboBootstrap.stop(); + DubboShutdownHook.getDubboShutdownHook().run(); } @Override diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapStartStopListenerSpringAdapter.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapStartStopListenerSpringAdapter.java new file mode 100644 index 0000000000..3ae3dcf521 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapStartStopListenerSpringAdapter.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.config.spring.context; + +import org.apache.dubbo.config.bootstrap.DubboBootstrap; +import org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener; +import org.apache.dubbo.config.spring.context.event.DubboBootstrapStatedEvent; +import org.apache.dubbo.config.spring.context.event.DubboBootstrapStopedEvent; + +import org.springframework.context.ApplicationContext; + +/** + * convcert Dubbo bootstrap event to spring environment. + * + * @scene 2.7.9 + */ +public class DubboBootstrapStartStopListenerSpringAdapter implements DubboBootstrapStartStopListener { + + static ApplicationContext applicationContext; + + @Override + public void onStart(DubboBootstrap bootstrap) { + if (applicationContext != null) { + applicationContext.publishEvent(new DubboBootstrapStatedEvent(bootstrap)); + } + } + + @Override + public void onStop(DubboBootstrap bootstrap) { + if (applicationContext != null) { + applicationContext.publishEvent(new DubboBootstrapStopedEvent(bootstrap)); + } + } +} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboLifecycleComponentApplicationListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboLifecycleComponentApplicationListener.java index 7e7ae83971..46cf1ceb7d 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboLifecycleComponentApplicationListener.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboLifecycleComponentApplicationListener.java @@ -19,6 +19,7 @@ package org.apache.dubbo.config.spring.context; import org.apache.dubbo.common.context.Lifecycle; +import com.alibaba.spring.context.OnceApplicationContextEventListener; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ApplicationContextEvent; @@ -39,7 +40,7 @@ import static org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncl * @see SmartApplicationListener * @since 2.7.5 */ -public class DubboLifecycleComponentApplicationListener extends OneTimeExecutionApplicationContextEventListener { +public class DubboLifecycleComponentApplicationListener extends OnceApplicationContextEventListener { /** * The bean name of {@link DubboLifecycleComponentApplicationListener} @@ -50,6 +51,13 @@ public class DubboLifecycleComponentApplicationListener extends OneTimeExecution private List lifecycleComponents = emptyList(); + public DubboLifecycleComponentApplicationListener() { + } + + public DubboLifecycleComponentApplicationListener(ApplicationContext applicationContext) { + super(applicationContext); + } + @Override protected void onApplicationContextEvent(ApplicationContextEvent event) { if (event instanceof ContextRefreshedEvent) { @@ -70,7 +78,6 @@ public class DubboLifecycleComponentApplicationListener extends OneTimeExecution private void initLifecycleComponents(ContextRefreshedEvent event) { ApplicationContext context = event.getApplicationContext(); - ClassLoader classLoader = context.getClassLoader(); lifecycleComponents = new LinkedList<>(); // load the Beans of Lifecycle from ApplicationContext loadLifecycleComponents(lifecycleComponents, context); diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/OneTimeExecutionApplicationContextEventListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/OneTimeExecutionApplicationContextEventListener.java deleted file mode 100644 index 569a67ec7c..0000000000 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/OneTimeExecutionApplicationContextEventListener.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.config.spring.context; - -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.ApplicationContextEvent; - -import java.util.Objects; - -/** - * The abstract class {@link ApplicationListener} for {@link ApplicationContextEvent} guarantees just one-time execution - * and prevents the event propagation in the hierarchical {@link ApplicationContext ApplicationContexts} - * - * @since 2.7.5 - */ -abstract class OneTimeExecutionApplicationContextEventListener implements ApplicationListener, ApplicationContextAware { - - private ApplicationContext applicationContext; - - public final void onApplicationEvent(ApplicationEvent event) { - if (isOriginalEventSource(event) && event instanceof ApplicationContextEvent) { - onApplicationContextEvent((ApplicationContextEvent) event); - } - } - - /** - * The subclass overrides this method to handle {@link ApplicationContextEvent} - * - * @param event {@link ApplicationContextEvent} - */ - protected abstract void onApplicationContextEvent(ApplicationContextEvent event); - - /** - * Is original {@link ApplicationContext} as the event source - * - * @param event {@link ApplicationEvent} - * @return - */ - private boolean isOriginalEventSource(ApplicationEvent event) { - return (applicationContext == null) // Current ApplicationListener is not a Spring Bean, just was added - // into Spring's ConfigurableApplicationContext - || Objects.equals(applicationContext, event.getSource()); - } - - @Override - public final void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - } - - public ApplicationContext getApplicationContext() { - return applicationContext; - } -} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfig.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfig.java index 1ec03e78bc..3d5e085b97 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfig.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfig.java @@ -24,6 +24,7 @@ import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; +import com.alibaba.spring.beans.factory.annotation.EnableConfigurationBeanBinding; import org.springframework.context.annotation.Import; import java.lang.annotation.Documented; @@ -34,7 +35,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * As a convenient and multiple {@link EnableDubboConfigBinding} + * As a convenient and multiple {@link EnableConfigurationBeanBinding} * in default behavior , is equal to single bean bindings with below convention prefixes of properties: *
      *
    • {@link ApplicationConfig} binding to property : "dubbo.application"
    • @@ -57,7 +58,7 @@ import java.lang.annotation.Target; *
    • {@link ConsumerConfig} binding to property : "dubbo.consumers"
    • *
    * - * @see EnableDubboConfigBinding + * @see EnableConfigurationBeanBinding * @see DubboConfigConfiguration * @see DubboConfigConfigurationRegistrar * @since 2.5.8 diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStatedEvent.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStatedEvent.java new file mode 100644 index 0000000000..11b0e2e180 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStatedEvent.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.config.spring.context.event; + +import org.apache.dubbo.config.bootstrap.DubboBootstrap; + +import org.springframework.context.ApplicationEvent; + +/** + * A {@link org.springframework.context.ApplicationEvent} after {@link org.apache.dubbo.config.bootstrap.DubboBootstrap#start()} success + * + * @see org.springframework.context.ApplicationEvent + * @see org.springframework.context.ApplicationListener + * @see org.apache.dubbo.config.bootstrap.DubboBootstrap + * @since 2.7.9 + */ +public class DubboBootstrapStatedEvent extends ApplicationEvent { + + /** + * Create a new ApplicationEvent. + * + * @param bootstrap {@link org.apache.dubbo.config.bootstrap.DubboBootstrap} bootstrap + */ + public DubboBootstrapStatedEvent(DubboBootstrap bootstrap) { + super(bootstrap); + } + + /** + * Get {@link org.apache.dubbo.config.bootstrap.DubboBootstrap} instance + * + * @return non-null + */ + public DubboBootstrap getDubboBootstrap() { + return (DubboBootstrap) super.getSource(); + } +} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStopedEvent.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStopedEvent.java new file mode 100644 index 0000000000..0f5b5bf306 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStopedEvent.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.config.spring.context.event; + +import org.apache.dubbo.config.bootstrap.DubboBootstrap; + +import org.springframework.context.ApplicationEvent; + +/** + * A {@link org.springframework.context.ApplicationEvent} after {@link org.apache.dubbo.config.bootstrap.DubboBootstrap#stop()} success + * + * @see org.springframework.context.ApplicationEvent + * @see org.springframework.context.ApplicationListener + * @see org.apache.dubbo.config.bootstrap.DubboBootstrap + * @since 2.7.9 + */ +public class DubboBootstrapStopedEvent extends ApplicationEvent { + + /** + * Create a new ApplicationEvent. + * + * @param bootstrap {@link org.apache.dubbo.config.bootstrap.DubboBootstrap} bootstrap + */ + public DubboBootstrapStopedEvent(DubboBootstrap bootstrap) { + super(bootstrap); + } + + /** + * Get {@link org.apache.dubbo.config.bootstrap.DubboBootstrap} instance + * + * @return non-null + */ + public DubboBootstrap getDubboBootstrap() { + return (DubboBootstrap) super.getSource(); + } +} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/properties/DefaultDubboConfigBinder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/properties/DefaultDubboConfigBinder.java index 1c87e9560f..e2d1e8ac10 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/properties/DefaultDubboConfigBinder.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/properties/DefaultDubboConfigBinder.java @@ -19,9 +19,13 @@ package org.apache.dubbo.config.spring.context.properties; import org.apache.dubbo.config.AbstractConfig; import org.springframework.beans.MutablePropertyValues; +import org.springframework.validation.BindingResult; import org.springframework.validation.DataBinder; +import org.springframework.validation.FieldError; +import java.util.List; import java.util.Map; +import java.util.stream.IntStream; import static com.alibaba.spring.util.PropertySourcesUtils.getSubProperties; @@ -42,7 +46,25 @@ public class DefaultDubboConfigBinder extends AbstractDubboConfigBinder { MutablePropertyValues propertyValues = new MutablePropertyValues(properties); // Bind dataBinder.bind(propertyValues); + BindingResult bindingResult = dataBinder.getBindingResult(); + if (bindingResult.hasGlobalErrors()) { + throw new RuntimeException("Data bind global error, please check config. config: " + bindingResult.getGlobalError() + ""); + } + if (bindingResult.hasFieldErrors()) { + throw new RuntimeException(buildErrorMsg(bindingResult.getFieldErrors(), prefix, dubboConfig.getClass().getSimpleName())); + } } + private String buildErrorMsg(List errors, String prefix, String config) { + StringBuilder builder = new StringBuilder("Data bind error, please check config. config: " + config + ", prefix: " + prefix + + " , error fields: [" + errors.get(0).getField()); + if (errors.size() > 1) { + IntStream.range(1, errors.size()).forEach(i -> { + builder.append(", " + errors.get(i).getField()); + }); + } + builder.append("]"); + return builder.toString(); + } } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java index c7b6e77c28..da887b2723 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java @@ -21,6 +21,7 @@ import org.apache.dubbo.common.extension.SPI; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashSet; +import org.apache.dubbo.config.DubboShutdownHook; import com.alibaba.spring.util.BeanFactoryUtils; import org.springframework.context.ApplicationContext; @@ -40,6 +41,8 @@ public class SpringExtensionFactory implements ExtensionFactory { CONTEXTS.add(context); if (context instanceof ConfigurableApplicationContext) { ((ConfigurableApplicationContext) context).registerShutdownHook(); + // see https://github.com/apache/dubbo/issues/7093 + DubboShutdownHook.getDubboShutdownHook().unregister(); } } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandler.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandler.java index a747e00237..71c10488c6 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandler.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandler.java @@ -66,7 +66,7 @@ public class DubboNamespaceHandler extends NamespaceHandlerSupport implements Co registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); - registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); + registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, true)); registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); } diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd index cf20146eaa..8ed2ac7bb6 100644 --- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd +++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd @@ -1079,6 +1079,11 @@ + + + + + @@ -1255,6 +1260,11 @@ + + + + + diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd index 57efa02b0a..0647827951 100644 --- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd +++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd @@ -710,6 +710,11 @@ + + + + + @@ -1101,6 +1106,11 @@ + + + + + @@ -1277,6 +1287,11 @@ + + + + + diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener new file mode 100644 index 0000000000..8775af2977 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener @@ -0,0 +1 @@ +default=org.apache.dubbo.config.spring.context.DubboBootstrapStartStopListenerSpringAdapter \ No newline at end of file diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockRegistryFactory.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockRegistryFactory.java index 8d8eb7774d..66f225cd4b 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockRegistryFactory.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockRegistryFactory.java @@ -26,20 +26,20 @@ import java.util.Map; public class MockRegistryFactory implements RegistryFactory { - private static final Map registries = new HashMap(); + private static final Map REGISTRIES = new HashMap(); public static Collection getCachedRegistry() { - return registries.values(); + return REGISTRIES.values(); } public static void cleanCachedRegistry() { - registries.clear(); + REGISTRIES.clear(); } @Override public Registry getRegistry(URL url) { MockRegistry registry = new MockRegistry(url); - registries.put(url, registry); + REGISTRIES.put(url, registry); return registry; } } diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-generic-consumer.xml b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-generic-consumer.xml index 2e4ea9cdfe..72bf442cb6 100644 --- a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-generic-consumer.xml +++ b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-generic-consumer.xml @@ -33,4 +33,4 @@ - \ No newline at end of file + diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-consumer-context.xml b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-consumer-context.xml index 9054c4f2ec..d7948cdc86 100644 --- a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-consumer-context.xml +++ b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-consumer-context.xml @@ -10,18 +10,18 @@ - - - - + - - \ No newline at end of file + diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-provider-context.xml b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-provider-context.xml index 207ab7b676..846c5ffc59 100644 --- a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-provider-context.xml +++ b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-provider-context.xml @@ -12,8 +12,8 @@ - - - \ No newline at end of file + + diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java index 64626f7764..dbf68221ce 100644 --- a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java +++ b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java @@ -42,11 +42,11 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.stream.Collectors; -import static org.apache.dubbo.common.config.configcenter.Constants.CONFIG_NAMESPACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; +import static org.apache.dubbo.common.constants.CommonConstants.CONFIG_NAMESPACE_KEY; /** * Apollo implementation, https://github.com/ctripcorp/apollo diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java b/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java index ca48bd6b9b..b14f172bb9 100644 --- a/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java +++ b/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java @@ -20,12 +20,11 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; -import com.ctrip.framework.apollo.mockserver.EmbeddedApollo; import com.google.common.util.concurrent.SettableFuture; -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.FileOutputStream; import java.io.IOException; @@ -51,13 +50,13 @@ public class ApolloDynamicConfigurationTest { /** * The constant embeddedApollo. */ - @ClassRule - public static EmbeddedApollo embeddedApollo = new EmbeddedApollo(); + @RegisterExtension + public static EmbeddedApolloJunit5 embeddedApollo = new EmbeddedApolloJunit5(); /** * Sets up. */ - @Before + @BeforeEach public void setUp() { String apolloUrl = System.getProperty("apollo.configService"); String urlForDubbo = "apollo://" + apolloUrl.substring(apolloUrl.lastIndexOf("/") + 1) + "/org.apache.dubbo.apollo.testService?namespace=dubbo&check=true"; @@ -183,7 +182,7 @@ public class ApolloDynamicConfigurationTest { /** * Tear down. */ - @After + @AfterEach public void tearDown() { } diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/EmbeddedApolloJunit5.java b/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/EmbeddedApolloJunit5.java new file mode 100644 index 0000000000..389d7c9b44 --- /dev/null +++ b/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/EmbeddedApolloJunit5.java @@ -0,0 +1,193 @@ +/* + * 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.configcenter.support.apollo; + +import com.ctrip.framework.apollo.build.ApolloInjector; +import com.ctrip.framework.apollo.core.dto.ApolloConfig; +import com.ctrip.framework.apollo.core.dto.ApolloConfigNotification; +import com.ctrip.framework.apollo.core.utils.ResourceUtils; +import com.ctrip.framework.apollo.internals.ConfigServiceLocator; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import okhttp3.mockwebserver.Dispatcher; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +public class EmbeddedApolloJunit5 implements BeforeAllCallback, AfterAllCallback { + private static final Logger logger = LoggerFactory.getLogger(EmbeddedApolloJunit5.class); + private static final Type notificationType = new TypeToken>() { + }.getType(); + + private static Method CONFIG_SERVICE_LOCATOR_CLEAR; + private static ConfigServiceLocator CONFIG_SERVICE_LOCATOR; + + private static final Gson GSON = new Gson(); + private final Map> addedOrModifiedPropertiesOfNamespace = Maps.newConcurrentMap(); + private final Map> deletedKeysOfNamespace = Maps.newConcurrentMap(); + + private MockWebServer server; + + static { + try { + System.setProperty("apollo.longPollingInitialDelayInMills", "0"); + CONFIG_SERVICE_LOCATOR = ApolloInjector.getInstance(ConfigServiceLocator.class); + CONFIG_SERVICE_LOCATOR_CLEAR = ConfigServiceLocator.class.getDeclaredMethod("initConfigServices"); + CONFIG_SERVICE_LOCATOR_CLEAR.setAccessible(true); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } + + private void clear() throws Exception { + resetOverriddenProperties(); + } + + private void mockConfigServiceUrl(String url) throws Exception { + System.setProperty("apollo.configService", url); + + CONFIG_SERVICE_LOCATOR_CLEAR.invoke(CONFIG_SERVICE_LOCATOR); + } + + private String loadConfigFor(String namespace) { + String filename = String.format("mockdata-%s.properties", namespace); + final Properties prop = ResourceUtils.readConfigFile(filename, new Properties()); + Map configurations = Maps.newHashMap(); + for (String propertyName : prop.stringPropertyNames()) { + configurations.put(propertyName, prop.getProperty(propertyName)); + } + ApolloConfig apolloConfig = new ApolloConfig("someAppId", "someCluster", namespace, "someReleaseKey"); + + Map mergedConfigurations = mergeOverriddenProperties(namespace, configurations); + apolloConfig.setConfigurations(mergedConfigurations); + return GSON.toJson(apolloConfig); + } + + private String mockLongPollBody(String notificationsStr) { + List oldNotifications = GSON.fromJson(notificationsStr, notificationType); + List newNotifications = new ArrayList<>(); + for (ApolloConfigNotification notification : oldNotifications) { + newNotifications + .add(new ApolloConfigNotification(notification.getNamespaceName(), notification.getNotificationId() + 1)); + } + return GSON.toJson(newNotifications); + } + + /** + * Incorporate user modifications to namespace + */ + private Map mergeOverriddenProperties(String namespace, Map configurations) { + if (addedOrModifiedPropertiesOfNamespace.containsKey(namespace)) { + configurations.putAll(addedOrModifiedPropertiesOfNamespace.get(namespace)); + } + if (deletedKeysOfNamespace.containsKey(namespace)) { + for (String k : deletedKeysOfNamespace.get(namespace)) { + configurations.remove(k); + } + } + return configurations; + } + + /** + * Add new property or update existed property + */ + public void addOrModifyProperty(String namespace, String someKey, String someValue) { + if (addedOrModifiedPropertiesOfNamespace.containsKey(namespace)) { + addedOrModifiedPropertiesOfNamespace.get(namespace).put(someKey, someValue); + } else { + Map m = Maps.newConcurrentMap(); + m.put(someKey, someValue); + addedOrModifiedPropertiesOfNamespace.put(namespace, m); + } + } + + /** + * Delete existed property + */ + public void deleteProperty(String namespace, String someKey) { + if (deletedKeysOfNamespace.containsKey(namespace)) { + deletedKeysOfNamespace.get(namespace).add(someKey); + } else { + Set m = Sets.newConcurrentHashSet(); + m.add(someKey); + deletedKeysOfNamespace.put(namespace, m); + } + } + + /** + * reset overridden properties + */ + public void resetOverriddenProperties() { + addedOrModifiedPropertiesOfNamespace.clear(); + deletedKeysOfNamespace.clear(); + } + + @Override + public void afterAll(ExtensionContext extensionContext) throws Exception { + try { + clear(); + server.close(); + } catch (Exception e) { + logger.error("stop apollo server error", e); + } + } + + @Override + public void beforeAll(ExtensionContext extensionContext) throws Exception { + clear(); + server = new MockWebServer(); + final Dispatcher dispatcher = new Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest request) throws InterruptedException { + if (request.getPath().startsWith("/notifications/v2")) { + String notifications = request.getRequestUrl().queryParameter("notifications"); + return new MockResponse().setResponseCode(200).setBody(mockLongPollBody(notifications)); + } + if (request.getPath().startsWith("/configs")) { + List pathSegments = request.getRequestUrl().pathSegments(); + // appId and cluster might be used in the future + String appId = pathSegments.get(1); + String cluster = pathSegments.get(2); + String namespace = pathSegments.get(3); + return new MockResponse().setResponseCode(200).setBody(loadConfigFor(namespace)); + } + return new MockResponse().setResponseCode(404); + } + }; + + server.setDispatcher(dispatcher); + server.start(); + + mockConfigServiceUrl("http://localhost:" + server.getPort()); + } +} diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java index a0acd51ef4..5a10be7ba8 100644 --- a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java +++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java @@ -80,9 +80,10 @@ public class CacheListener implements DataListener { if (!StringUtils.isEmpty(path)) { int beginIndex = path.indexOf(rootPath + PATH_SEPARATOR); if (beginIndex > -1) { - int endIndex = path.indexOf(PATH_SEPARATOR, beginIndex); - if (endIndex > beginIndex) { - return path.substring(beginIndex, endIndex); + String remain = path.substring((rootPath + PATH_SEPARATOR).length()); + int endIndex = remain.lastIndexOf(PATH_SEPARATOR); + if (endIndex > -1) { + return remain.substring(0, endIndex); } } } diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java index a4c35110b4..3ce72c0a86 100644 --- a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java +++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigItem; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.TreePathDynamicConfiguration; +import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.remoting.zookeeper.ZookeeperClient; import org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter; @@ -27,7 +28,8 @@ import org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter; import java.util.Collection; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; -import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** @@ -43,6 +45,9 @@ public class ZookeeperDynamicConfiguration extends TreePathDynamicConfiguration private CacheListener cacheListener; private URL url; + private static final int DEFAULT_ZK_EXECUTOR_THREADS_NUM = 1; + private static final int DEFAULT_QUEUE = 10000; + private static final Long THREAD_KEEP_ALIVE_TIME = 0L; ZookeeperDynamicConfiguration(URL url, ZookeeperTransporter zookeeperTransporter) { @@ -52,7 +57,13 @@ public class ZookeeperDynamicConfiguration extends TreePathDynamicConfiguration initializedLatch = new CountDownLatch(1); this.cacheListener = new CacheListener(rootPath, initializedLatch); - this.executor = Executors.newFixedThreadPool(1, new NamedThreadFactory(this.getClass().getSimpleName(), true)); + + final String threadName = this.getClass().getSimpleName(); + this.executor = new ThreadPoolExecutor(DEFAULT_ZK_EXECUTOR_THREADS_NUM, DEFAULT_ZK_EXECUTOR_THREADS_NUM, + THREAD_KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue(DEFAULT_QUEUE), + new NamedThreadFactory(threadName, true), + new AbortPolicyWithReport(threadName, url)); zkClient = zookeeperTransporter.connect(url); zkClient.addDataListener(rootPath, cacheListener, executor); diff --git a/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/start.sh b/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/start.sh index 50c3a708e6..b87ac79e8f 100755 --- a/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/start.sh +++ b/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/start.sh @@ -21,11 +21,11 @@ cd .. DEPLOY_DIR=`pwd` CONF_DIR=$DEPLOY_DIR/conf -SERVER_NAME=`sed '/dubbo.application.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` -SERVER_PROTOCOL=`sed '/dubbo.protocol.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` -SERVER_HOST=`sed '/dubbo.protocol.host/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` -SERVER_PORT=`sed '/dubbo.protocol.port/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` -LOGS_FILE=`sed '/dubbo.log4j.file/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` +SERVER_NAME=`sed '/^dubbo.application.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` +SERVER_PROTOCOL=`sed '/^dubbo.protocol.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` +SERVER_HOST=`sed '/^dubbo.protocol.host/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` +SERVER_PORT=`sed '/^dubbo.protocol.port/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` +LOGS_FILE=`sed '/^dubbo.log4j.file/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` VM_ARGS_PERM_SIZE='PermSize' VM_ARGS_METASPACE_SIZE='MetaspaceSize' JAVA_8_VERSION="180" diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml index fffa2bd1c1..3bf2035854 100644 --- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml +++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml @@ -79,5 +79,9 @@ org.apache.dubbo dubbo-serialization-hessian2 + + org.apache.dubbo + dubbo-serialization-jdk + diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml index c8cf1383f9..eda09798a9 100644 --- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml +++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml @@ -88,7 +88,7 @@ org.apache.dubbo - dubbo-qos + dubbo-serialization-jdk org.slf4j diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml index 60d1491d72..5cf588e5b2 100644 --- a/dubbo-dependencies-bom/pom.xml +++ b/dubbo-dependencies-bom/pom.xml @@ -90,7 +90,7 @@ 5.2.8.RELEASE - 3.20.0-GA + 3.23.1-GA 10.4.0 3.2.5.Final 4.1.56.Final @@ -102,7 +102,7 @@ 3.4.13 4.0.1 2.12.0 - 2.9.0 + 3.6.0 1.4.2 2.0.0 1.3.7 @@ -155,7 +155,7 @@ 2.2.7 1.2.0 - 1.11.2 + 1.15.3 0.5.3 3.2.8 1.5.19 diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java b/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java index 02d0e299a0..3122a8eee4 100644 --- a/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java +++ b/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java @@ -37,7 +37,7 @@ import static org.apache.dubbo.common.constants.FilterConstants.CACHE_KEY; /** * CacheFilter is a core component of dubbo.Enabling cache key of service,method,consumer or provider dubbo will cache method return value. * Along with cache key we need to configure cache type. Dubbo default implemented cache types are - *
  • lur
  • + *
  • lru
  • *
  • threadlocal
  • *
  • jcache
  • *
  • expiring
  • diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/AbstractCacheFactory.java b/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/AbstractCacheFactory.java index 966d923a78..17530af874 100644 --- a/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/AbstractCacheFactory.java +++ b/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/AbstractCacheFactory.java @@ -44,9 +44,12 @@ public abstract class AbstractCacheFactory implements CacheFactory { */ private final ConcurrentMap caches = new ConcurrentHashMap(); + private final Object MONITOR = new Object(); + /** - * Takes URL and invocation instance and return cache instance for a given url. - * @param url url of the method + * Takes URL and invocation instance and return cache instance for a given url. + * + * @param url url of the method * @param invocation invocation context. * @return Instance of cache store used as storage for caching return values. */ @@ -55,15 +58,29 @@ public abstract class AbstractCacheFactory implements CacheFactory { url = url.addParameter(METHOD_KEY, invocation.getMethodName()); String key = url.toFullString(); Cache cache = caches.get(key); - if (cache == null) { - caches.put(key, createCache(url)); - cache = caches.get(key); + + // get from cache first. + if (null != cache) { + return cache; } + + synchronized (MONITOR) { + // double check. + cache = caches.get(key); + if (null != cache) { + return cache; + } + + cache = createCache(url); + caches.put(key, cache); + } + return cache; } /** * Takes url as an method argument and return new instance of cache store implemented by AbstractCacheFactory subclass. + * * @param url url of the method * @return Create and return new instance of cache store used as storage for caching return values. */ diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringMap.java b/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringMap.java index 895f11408c..b5306e2243 100644 --- a/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringMap.java +++ b/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringMap.java @@ -84,6 +84,12 @@ public class ExpiringMap implements Map { public V get(Object key) { ExpiryObject object = delegateMap.get(key); if (object != null) { + long timeIdle = System.currentTimeMillis() - object.getLastAccessTime(); + int timeToLive = expireThread.getTimeToLive(); + if (timeToLive > 0 && timeIdle >= timeToLive * 1000) { + delegateMap.remove(object.getKey()); + return null; + } object.setLastAccessTime(System.currentTimeMillis()); return object.getValue(); } @@ -137,6 +143,9 @@ public class ExpiringMap implements Map { @Override public boolean equals(Object obj) { + if (this == obj) { + return true; + } return delegateMap.equals(obj); } @@ -226,6 +235,9 @@ public class ExpiringMap implements Map { @Override public boolean equals(Object obj) { + if (this == obj) { + return true; + } return value.equals(obj); } @@ -282,10 +294,10 @@ public class ExpiringMap implements Map { private void processExpires() { long timeNow = System.currentTimeMillis(); + if (timeToLiveMillis <= 0) { + return; + } for (ExpiryObject o : delegateMap.values()) { - if (timeToLiveMillis <= 0) { - continue; - } long timeIdle = timeNow - o.getLastAccessTime(); if (timeIdle >= timeToLiveMillis) { delegateMap.remove(o.getKey()); @@ -307,7 +319,7 @@ public class ExpiringMap implements Map { * start thread */ public void startExpiryIfNotStarted() { - if (running) { + if (running && timeToLiveMillis <= 0) { return; } startExpiring(); diff --git a/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory b/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory index 1ea180adf5..aa72c929f1 100644 --- a/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory +++ b/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory @@ -1,4 +1,5 @@ threadlocal=org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory lru=org.apache.dubbo.cache.support.lru.LruCacheFactory jcache=org.apache.dubbo.cache.support.jcache.JCacheFactory -expiring=org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory \ No newline at end of file +expiring=org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory +lfu=org.apache.dubbo.cache.support.lfu.LfuCacheFactory diff --git a/dubbo-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java b/dubbo-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java index 23484c6c32..d793460d85 100644 --- a/dubbo-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java +++ b/dubbo-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java @@ -19,18 +19,90 @@ package org.apache.dubbo.cache.support.expiring; import org.apache.dubbo.cache.Cache; import org.apache.dubbo.cache.support.AbstractCacheFactory; import org.apache.dubbo.cache.support.AbstractCacheFactoryTest; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.RpcInvocation; + import org.junit.jupiter.api.Test; -import static org.hamcrest.core.Is.is; +import java.util.concurrent.TimeUnit; + import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; public class ExpiringCacheFactoryTest extends AbstractCacheFactoryTest { + + private static final String EXPIRING_CACHE_URL = + "test://test:12/test?cache=expiring&cache.seconds=1&cache.interval=1"; + @Test - public void testLruCacheFactory() throws Exception { + public void testExpiringCacheFactory() throws Exception { Cache cache = super.constructCache(); assertThat(cache instanceof ExpiringCache, is(true)); } + @Test + public void testExpiringCacheGetExpired() throws Exception { + URL url = URL.valueOf("test://test:12/test?cache=expiring&cache.seconds=1&cache.interval=1"); + AbstractCacheFactory cacheFactory = getCacheFactory(); + Invocation invocation = new RpcInvocation(); + Cache cache = cacheFactory.getCache(url, invocation); + cache.put("testKey", "testValue"); + Thread.sleep(2100); + assertNull(cache.get("testKey")); + } + + @Test + public void testExpiringCacheUnExpired() throws Exception { + URL url = URL.valueOf("test://test:12/test?cache=expiring&cache.seconds=0&cache.interval=1"); + AbstractCacheFactory cacheFactory = getCacheFactory(); + Invocation invocation = new RpcInvocation(); + Cache cache = cacheFactory.getCache(url, invocation); + cache.put("testKey", "testValue"); + Thread.sleep(1100); + assertNotNull(cache.get("testKey")); + } + + @Test + public void testExpiringCache() throws Exception { + Cache cache = constructCache(); + assertThat(cache instanceof ExpiringCache, is(true)); + + // 500ms + TimeUnit.MILLISECONDS.sleep(500); + cache.put("testKey", "testValue"); + // 800ms + TimeUnit.MILLISECONDS.sleep(300); + assertNotNull(cache.get("testKey")); + // 1300ms + TimeUnit.MILLISECONDS.sleep(500); + assertNotNull(cache.get("testKey")); + } + + @Test + public void testExpiringCacheExpired() throws Exception { + Cache cache = constructCache(); + assertThat(cache instanceof ExpiringCache, is(true)); + + // 500ms + TimeUnit.MILLISECONDS.sleep(500); + cache.put("testKey", "testValue"); + // 1000ms ExpireThread clear all expire cache + TimeUnit.MILLISECONDS.sleep(500); + // 1700ms get should be null + TimeUnit.MILLISECONDS.sleep(700); + assertNull(cache.get("testKey")); + } + + @Override + protected Cache constructCache() { + URL url = URL.valueOf(EXPIRING_CACHE_URL); + Invocation invocation = new RpcInvocation(); + return getCacheFactory().getCache(url, invocation); + } + @Override protected AbstractCacheFactory getCacheFactory() { return new ExpiringCacheFactory(); diff --git a/dubbo-metadata/dubbo-metadata-api/pom.xml b/dubbo-metadata/dubbo-metadata-api/pom.xml index fe6a20c190..9caf31e432 100644 --- a/dubbo-metadata/dubbo-metadata-api/pom.xml +++ b/dubbo-metadata/dubbo-metadata-api/pom.xml @@ -78,6 +78,13 @@ test
    + + + org.springframework + spring-context + test + + \ No newline at end of file diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java index f524fcf7d3..44b8000f9d 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java @@ -69,7 +69,7 @@ public class DynamicConfigurationServiceNameMapping implements ServiceNameMappin dynamicConfiguration.publishConfig(key, ServiceNameMapping.buildGroup(serviceInterface, group, version, protocol), content); if (logger.isDebugEnabled()) { logger.info(String.format("Dubbo service[%s] mapped to interface name[%s].", - group, serviceInterface, group)); + group, serviceInterface)); } }); } 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 cda2e587db..cac87d2979 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 @@ -18,7 +18,6 @@ package org.apache.dubbo.metadata.definition.builder; import org.apache.dubbo.metadata.definition.TypeDefinitionBuilder; import org.apache.dubbo.metadata.definition.model.TypeDefinition; -import org.apache.dubbo.metadata.definition.util.ClassUtils; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -26,7 +25,6 @@ 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; @@ -60,14 +58,7 @@ public class MapTypeBuilder implements TypeBuilder { + Arrays.toString(actualTypeArgs), type, actualTypeArgs)); } - // Change since 2.7.6 - /** - * Replacing ", " to "," will not change the semantic of - * {@link ParameterizedType#toString()} - * @see sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl - */ - String mapType = ClassUtils.getCanonicalNameForParameterizedType(parameterizedType); - mapType = replace(mapType, ", ", ","); + String mapType = type.toString(); TypeDefinition td = typeCache.get(mapType); if (td != null) { diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/MethodDefinition.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/MethodDefinition.java index ac09e962b5..e3558599d9 100755 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/MethodDefinition.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/MethodDefinition.java @@ -22,6 +22,9 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; +import static org.apache.dubbo.metadata.definition.model.TypeDefinition.formatType; +import static org.apache.dubbo.metadata.definition.model.TypeDefinition.formatTypes; + /** * 2015/1/27. */ @@ -66,11 +69,11 @@ public class MethodDefinition implements Serializable { } public void setParameterTypes(String[] parameterTypes) { - this.parameterTypes = parameterTypes; + this.parameterTypes = formatTypes(parameterTypes); } public void setReturnType(String returnType) { - this.returnType = returnType; + this.returnType = formatType(returnType); } @Override diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/TypeDefinition.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/TypeDefinition.java index 816bd097aa..6dd0f0625d 100755 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/TypeDefinition.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/TypeDefinition.java @@ -26,6 +26,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import static org.apache.dubbo.common.utils.StringUtils.replace; + /** * 2015/1/27. */ @@ -65,7 +67,52 @@ public class TypeDefinition implements Serializable { } public TypeDefinition(String type) { - this.type = type; + this.setType(type); + } + + /** + * Format the {@link String} array presenting Java types + * + * @param types the strings presenting Java types + * @return new String array of Java types after be formatted + * @since 2.7.9 + */ + public static String[] formatTypes(String[] types) { + String[] newTypes = new String[types.length]; + for (int i = 0; i < types.length; i++) { + newTypes[i] = formatType(types[i]); + } + return newTypes; + } + + /** + * Format the {@link String} presenting Java type + * + * @param type the String presenting type + * @return new String presenting Java type after be formatted + * @since 2.7.9 + */ + public static String formatType(String type) { + if (isGenericType(type)) { + return formatGenericType(type); + } + return type; + } + + /** + * Replacing ", " to "," will not change the semantic of + * {@link ParameterizedType#toString()} + * + * @param type + * @return formatted type + * @see sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl + */ + private static String formatGenericType(String type) { + return replace(type, ", ", ","); + } + + private static boolean isGenericType(String type) { + return type.contains("<") && type.contains(">"); } public List getEnums() { @@ -106,7 +153,7 @@ public class TypeDefinition implements Serializable { } public void setType(String type) { - this.type = type; + this.type = formatType(type); } @Override diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java index a0e360fa9d..4cdbb3b951 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java @@ -20,11 +20,13 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.MetadataReportConfig; +import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; +import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_DIRECTORY; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY; import static org.apache.dubbo.metadata.report.support.Constants.METADATA_REPORT_KEY; @@ -51,6 +53,7 @@ public class MetadataReportInstance { .removeParameter(METADATA_REPORT_KEY) .build(); } + url = url.addParameterIfAbsent(APPLICATION_KEY, ApplicationModel.getApplicationConfig().getName()); String relatedRegistryId = config.getRegistry() == null ? DEFAULT_KEY : config.getRegistry(); // RegistryConfig registryConfig = ApplicationModel.getConfigManager().getRegistry(relatedRegistryId) // .orElseThrow(() -> new IllegalStateException("Registry id " + relatedRegistryId + " does not exist.")); diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportFactory.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportFactory.java index 454b250516..07b26f0aab 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportFactory.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportFactory.java @@ -39,10 +39,16 @@ public abstract class AbstractMetadataReportFactory implements MetadataReportFac url = url.setPath(MetadataReport.class.getName()) .removeParameters(EXPORT_KEY, REFER_KEY); String key = url.toServiceString(); + + MetadataReport metadataReport = SERVICE_STORE_MAP.get(key); + if (metadataReport != null) { + return metadataReport; + } + // Lock the metadata access process to ensure a single instance of the metadata instance LOCK.lock(); try { - MetadataReport metadataReport = SERVICE_STORE_MAP.get(key); + metadataReport = SERVICE_STORE_MAP.get(key); if (metadataReport != null) { return metadataReport; } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java index ea074214ec..eb48bdaa21 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java @@ -18,6 +18,7 @@ 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.DubboService; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.metadata.definition.MethodDefinitionBuilder; import org.apache.dubbo.metadata.definition.model.MethodDefinition; @@ -70,7 +71,8 @@ public abstract class AbstractServiceRestMetadataResolver implements ServiceRest } protected final boolean isServiceAnnotationPresent(Class serviceType) { - return isAnyAnnotationPresent(serviceType, Service.class, com.alibaba.dubbo.config.annotation.Service.class); + return isAnyAnnotationPresent(serviceType, DubboService.class, Service.class, + com.alibaba.dubbo.config.annotation.Service.class); } /** @@ -296,7 +298,7 @@ public abstract class AbstractServiceRestMetadataResolver implements ServiceRest for (int i = 0; i < paramCount; i++) { Parameter parameter = parameters[i]; // Add indexed parameter name - metadata.addIndexToName(i,parameter.getName()); + metadata.addIndexToName(i, parameter.getName()); processAnnotatedMethodParameter(parameter, i, serviceMethod, serviceType, serviceInterfaceClass, metadata); } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java index 82e6328574..6c758bf23b 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java @@ -16,6 +16,11 @@ */ package org.apache.dubbo.metadata.rest; +import java.lang.annotation.Annotation; + +import static org.apache.dubbo.common.utils.ClassUtils.getClassLoader; +import static org.apache.dubbo.common.utils.ClassUtils.resolveClass; + /** * The REST Metadata Constants definition interface * @@ -108,5 +113,33 @@ public interface RestMetadataConstants { * The annotation class name of @RequestParam */ String REQUEST_PARAM_ANNOTATION_CLASS_NAME = "org.springframework.web.bind.annotation.RequestParam"; + + /** + * The class of @Controller + * + * @since 2.7.9 + */ + Class CONTROLLER_ANNOTATION_CLASS = (Class) resolveClass(CONTROLLER_ANNOTATION_CLASS_NAME, getClassLoader()); + + /** + * The class of @RequestMapping + * + * @since 2.7.9 + */ + Class REQUEST_MAPPING_ANNOTATION_CLASS = (Class) resolveClass(REQUEST_MAPPING_ANNOTATION_CLASS_NAME, getClassLoader()); + + /** + * The annotation class name of AnnotatedElementUtils + * + * @since 2.7.9 + */ + String ANNOTATED_ELEMENT_UTILS_CLASS_NAME = "org.springframework.core.annotation.AnnotatedElementUtils"; + + /** + * The class of AnnotatedElementUtils + * + * @since 2.7.9 + */ + Class ANNOTATED_ELEMENT_UTILS_CLASS = resolveClass(ANNOTATED_ELEMENT_UTILS_CLASS_NAME, getClassLoader()); } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java index a675eb3da0..1531c973fd 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java @@ -159,8 +159,12 @@ public class RestMethodMetadata implements Serializable { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof RestMethodMetadata)) return false; + 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()) && diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java index 876b8a354a..df396b0d70 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java @@ -76,8 +76,12 @@ public class ServiceRestMetadata implements Serializable { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ServiceRestMetadata)) return false; + 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()) && 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 index 28407c267a..74807ea280 100644 --- 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 @@ -16,11 +16,13 @@ */ package org.apache.dubbo.metadata.rest.springmvc; +import org.apache.dubbo.common.utils.AnnotationUtils; 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 java.util.Objects; import static org.apache.dubbo.common.utils.AnnotationUtils.getAttribute; @@ -49,11 +51,18 @@ public abstract class AbstractRequestAnnotationParameterProcessor extends Abstra @Override protected String getDefaultValue(Annotation annotation, Parameter parameter, int parameterIndex) { - String defaultValue = getAttribute(annotation, "defaultValue"); - if (isEmpty(defaultValue)) { - defaultValue = super.getDefaultValue(annotation, parameter, parameterIndex); + String attributeName = "defaultValue"; + String attributeValue = getAttribute(annotation, attributeName); + + if (isEmpty(attributeValue) || isDefaultValue(annotation, attributeName, attributeValue)) { + attributeValue = super.getDefaultValue(annotation, parameter, parameterIndex); } - return defaultValue; + return attributeValue; + } + + private boolean isDefaultValue(Annotation annotation, String attributeName, Object attributeValue) { + String defaultValue = AnnotationUtils.getDefaultValue(annotation, attributeName); + return Objects.equals(attributeValue, defaultValue); } protected boolean isEmpty(String str) { diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java index e24422a746..de64592ab8 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java @@ -29,14 +29,15 @@ 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.MethodUtils.findMethod; import static org.apache.dubbo.common.utils.PathUtils.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; +import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.ANNOTATED_ELEMENT_UTILS_CLASS; +import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.CONTROLLER_ANNOTATION_CLASS; +import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_MAPPING_ANNOTATION_CLASS; /** * {@link ServiceRestMetadataResolver} @@ -49,23 +50,17 @@ public class SpringMvcServiceRestMetadataResolver extends AbstractServiceRestMet @Override protected boolean supports0(Class serviceType) { - return isAnnotationPresent(serviceType, CONTROLLER_ANNOTATION_CLASS_NAME); + return isAnnotationPresent(serviceType, CONTROLLER_ANNOTATION_CLASS); } @Override protected boolean isRestCapableMethod(Method serviceMethod, Class serviceType, Class serviceInterfaceClass) { - return isAnnotationPresent(serviceType, REQUEST_MAPPING_ANNOTATION_CLASS_NAME); + return isAnnotationPresent(serviceType, REQUEST_MAPPING_ANNOTATION_CLASS) || + isAnnotationPresent(serviceMethod, REQUEST_MAPPING_ANNOTATION_CLASS); } @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 @@ -79,6 +74,13 @@ public class SpringMvcServiceRestMetadataResolver extends AbstractServiceRestMet return valueOf(Array.get(httpMethod, FIRST_ELEMENT_INDEX)); } + @Override + protected String resolveRequestPath(Method serviceMethod, Class serviceType, Class serviceInterfaceClass) { + String requestBasePath = resolveRequestPath(serviceType); + String requestRelativePath = resolveRequestPath(serviceMethod); + return buildPath(requestBasePath, requestRelativePath); + } + @Override protected void processProduces(Method serviceMethod, Class serviceType, Class serviceInterfaceClass, Set produces) { addMediaTypes(serviceMethod, "produces", produces); @@ -91,6 +93,7 @@ public class SpringMvcServiceRestMetadataResolver extends AbstractServiceRestMet private String resolveRequestPath(AnnotatedElement annotatedElement) { Annotation mappingAnnotation = getRequestMapping(annotatedElement); + // try "value" first String[] value = getAttribute(mappingAnnotation, "value"); @@ -118,10 +121,20 @@ public class SpringMvcServiceRestMetadataResolver extends AbstractServiceRestMet private Annotation getRequestMapping(AnnotatedElement annotatedElement) { // try "@RequestMapping" first - Annotation requestMapping = findAnnotation(annotatedElement, REQUEST_MAPPING_ANNOTATION_CLASS_NAME); - // try the annotation meta-annotated later + Annotation requestMapping = findAnnotation(annotatedElement, REQUEST_MAPPING_ANNOTATION_CLASS); if (requestMapping == null) { - requestMapping = findMetaAnnotation(annotatedElement, REQUEST_MAPPING_ANNOTATION_CLASS_NAME); + // To try the meta-annotated annotation if can't be found. + // For example, if the annotation "@GetMapping" is used in the Spring Framework is 4.2 or above, + // because of "@GetMapping" alias for ("@AliasFor") "@RequestMapping" , both of them belongs to + // the artifact "spring-web" which depends on "spring-core", thus Spring core's + // AnnotatedElementUtils.findMergedAnnotation(AnnotatedElement, Class) must be involved. + Method method = findMethod(ANNOTATED_ELEMENT_UTILS_CLASS, "findMergedAnnotation", AnnotatedElement.class, Class.class); + if (method != null) { + try { + requestMapping = (Annotation) method.invoke(null, annotatedElement, REQUEST_MAPPING_ANNOTATION_CLASS); + } catch (Exception ignored) { + } + } } return requestMapping; } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java index ead518641d..f967ad7c5f 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java @@ -16,7 +16,7 @@ */ package org.apache.dubbo.metadata.rest; -import org.apache.dubbo.config.annotation.Service; +import org.apache.dubbo.config.annotation.DubboService; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; @@ -35,7 +35,7 @@ import java.util.Map; * * @since 2.7.6 */ -@Service(version = "2.0.0") +@DubboService(version = "2.0.0", group = "spring") @RestController public class SpringRestService implements RestService { diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java index f5d94b7c1e..684d76fd3b 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java @@ -16,7 +16,7 @@ */ package org.apache.dubbo.metadata.rest; -import org.apache.dubbo.config.annotation.Service; +import org.apache.dubbo.config.annotation.DubboService; import javax.ws.rs.Consumes; import javax.ws.rs.FormParam; @@ -34,7 +34,7 @@ import java.util.Map; /** * JAX-RS {@link RestService} */ -@Service(version = "3.0.0", protocol = {"dubbo", "rest"}, group = "standard") +@DubboService(version = "3.0.0", protocol = {"dubbo", "rest"}, group = "standard") @Path("/") public class StandardRestService implements RestService { 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/jaxrs/JAXRSServiceRestMetadataResolverTest.java similarity index 96% rename from dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/resolver/jaxrs/JAXRSServiceRestMetadataResolverTest.java rename to dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolverTest.java index d959007aba..53c5428867 100644 --- 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/jaxrs/JAXRSServiceRestMetadataResolverTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.metadata.rest.resolver.jaxrs; +package org.apache.dubbo.metadata.rest.jaxrs; import org.apache.dubbo.metadata.rest.ClassPathServiceRestMetadataReader; import org.apache.dubbo.metadata.rest.DefaultRestService; @@ -23,7 +23,6 @@ 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; diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java new file mode 100644 index 0000000000..43abc8f2a3 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata.rest.springmvc; + +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.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 SpringMvcServiceRestMetadataResolver} Test + * + * @since 2.7.9 + */ +public class SpringMvcServiceRestMetadataResolverTest { + + private SpringMvcServiceRestMetadataResolver instance = new SpringMvcServiceRestMetadataResolver(); + + @Test + public void testSupports() { + // Spring MVC RestService class + assertTrue(instance.supports(SpringRestService.class)); + // JAX-RS RestService class + assertFalse(instance.supports(StandardRestService.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/spring-mvc-servoce-rest-metadata.json"); + List serviceRestMetadataList = reader.read(); + + ServiceRestMetadata expectedServiceRestMetadata = serviceRestMetadataList.get(0); + ServiceRestMetadata serviceRestMetadata = instance.resolve(SpringRestService.class); + + + List meta1 = new LinkedList<>(expectedServiceRestMetadata.getMeta()); + List 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/spring-mvc-servoce-rest-metadata.json b/dubbo-metadata/dubbo-metadata-api/src/test/resources/META-INF/dubbo/spring-mvc-servoce-rest-metadata.json new file mode 100644 index 0000000000..c0f25f59a5 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/test/resources/META-INF/dubbo/spring-mvc-servoce-rest-metadata.json @@ -0,0 +1,321 @@ +[ + { + "serviceInterface": "org.apache.dubbo.metadata.rest.RestService", + "version": "2.0.0", + "group": "spring", + "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": [ + "1" + ] + }, + "headers": { + "h": [ + "value-h" + ], + "h2": [ + "value-h2" + ] + }, + "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": [ + "value-param" + ] + }, + "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": [ + "value-a" + ], + "b": [ + "value-b" + ] + }, + "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": ["java.lang.String", "java.lang.Object"], + "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": "java.lang.String", + "id": "java.lang.Long", + "age": "java.lang.Integer" + } + } + ] + }, + "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 8d789f73ab..e10ffa33ea 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 @@ -21,6 +21,7 @@ import org.apache.dubbo.metadata.definition.model.FullServiceDefinition; import org.apache.dubbo.metadata.definition.model.MethodDefinition; import org.apache.dubbo.metadata.definition.model.TypeDefinition; import org.apache.dubbo.metadata.definition.protobuf.model.ServiceInterface; + import org.junit.jupiter.api.Test; import java.util.List; @@ -70,7 +71,7 @@ public class ProtobufTypeBuilderTest { equalTo("java.util.List")); assertThat(propertiesMap.containsKey("doubleMap"), is(true)); assertThat(getTypeName(propertiesMap.get("doubleMap"), types), - equalTo("java.util.Map")); + equalTo("java.util.Map")); assertThat(getTypeName(propertiesMap.get("bytesList"), types), equalTo("java.util.List")); assertThat(getTypeName(propertiesMap.get("bytesMap"), types), diff --git a/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtils.java b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtils.java index b4aaffdd71..6f0010b550 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtils.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtils.java @@ -18,7 +18,8 @@ package org.apache.dubbo.metadata.annotation.processing.util; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.TypeElement; -import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Objects; import java.util.Set; import static java.lang.String.valueOf; @@ -36,15 +37,27 @@ public interface ServiceAnnotationUtils { /** * The class name of @Service + * + * @deprecated Recommend {@link #DUBBO_SERVICE_ANNOTATION_TYPE} */ + @Deprecated String SERVICE_ANNOTATION_TYPE = "org.apache.dubbo.config.annotation.Service"; /** * The class name of the legacy @Service + * + * @deprecated Recommend {@link #DUBBO_SERVICE_ANNOTATION_TYPE} */ @Deprecated String LEGACY_SERVICE_ANNOTATION_TYPE = "com.alibaba.dubbo.config.annotation.Service"; + /** + * The class name of @DubboService + * + * @since 2.7.9 + */ + String DUBBO_SERVICE_ANNOTATION_TYPE = "org.apache.dubbo.config.annotation.DubboService"; + /** * the attribute name of @Service.interfaceClass() */ @@ -65,11 +78,13 @@ public interface ServiceAnnotationUtils { */ String VERSION_ATTRIBUTE_NAME = "version"; - Set SUPPORTED_ANNOTATION_TYPES = unmodifiableSet(new HashSet(asList(SERVICE_ANNOTATION_TYPE, LEGACY_SERVICE_ANNOTATION_TYPE))); + Set SUPPORTED_ANNOTATION_TYPES = unmodifiableSet(new LinkedHashSet<>(asList(DUBBO_SERVICE_ANNOTATION_TYPE, SERVICE_ANNOTATION_TYPE, LEGACY_SERVICE_ANNOTATION_TYPE))); static boolean isServiceAnnotationPresent(TypeElement annotatedType) { - return isAnnotationPresent(annotatedType, SERVICE_ANNOTATION_TYPE) || - isAnnotationPresent(annotatedType, LEGACY_SERVICE_ANNOTATION_TYPE); + return SUPPORTED_ANNOTATION_TYPES.stream() + .filter(type -> isAnnotationPresent(annotatedType, type)) + .findFirst() + .isPresent(); } static AnnotationMirror getAnnotation(TypeElement annotatedClass) { @@ -78,19 +93,20 @@ public interface ServiceAnnotationUtils { static AnnotationMirror getAnnotation(Iterable annotationMirrors) { AnnotationMirror matchedAnnotationMirror = null; - for (AnnotationMirror annotationMirror : annotationMirrors) { - String annotationType = annotationMirror.getAnnotationType().toString(); - if (SERVICE_ANNOTATION_TYPE.equals(annotationType)) { - matchedAnnotationMirror = annotationMirror; - break; - } else if (LEGACY_SERVICE_ANNOTATION_TYPE.equals(annotationType)) { - matchedAnnotationMirror = annotationMirror; + + MAIN: + for (String supportedAnnotationType : SUPPORTED_ANNOTATION_TYPES) { // Prioritized + for (AnnotationMirror annotationMirror : annotationMirrors) { + String annotationType = annotationMirror.getAnnotationType().toString(); + if (Objects.equals(supportedAnnotationType, annotationType)) { + matchedAnnotationMirror = annotationMirror; + break MAIN; + } } } if (matchedAnnotationMirror == null) { - throw new IllegalArgumentException("The annotated element must be implemented the interface " - + SERVICE_ANNOTATION_TYPE + " or " + LEGACY_SERVICE_ANNOTATION_TYPE); + throw new IllegalArgumentException("The annotated element must be annotated any of " + SUPPORTED_ANNOTATION_TYPES); } return matchedAnnotationMirror; diff --git a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtilsTest.java b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtilsTest.java index 9d18ddf681..e0e95a45c9 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtilsTest.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtilsTest.java @@ -25,10 +25,11 @@ import org.apache.dubbo.metadata.tools.TestServiceImpl; import org.junit.jupiter.api.Test; import javax.lang.model.element.TypeElement; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import static java.util.Arrays.asList; +import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.DUBBO_SERVICE_ANNOTATION_TYPE; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.GROUP_ATTRIBUTE_NAME; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.INTERFACE_CLASS_ATTRIBUTE_NAME; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.INTERFACE_NAME_ATTRIBUTE_NAME; @@ -65,13 +66,14 @@ public class ServiceAnnotationUtilsTest extends AbstractAnnotationProcessingTest @Test public void testConstants() { + assertEquals("org.apache.dubbo.config.annotation.DubboService", DUBBO_SERVICE_ANNOTATION_TYPE); assertEquals("org.apache.dubbo.config.annotation.Service", SERVICE_ANNOTATION_TYPE); assertEquals("com.alibaba.dubbo.config.annotation.Service", LEGACY_SERVICE_ANNOTATION_TYPE); assertEquals("interfaceClass", INTERFACE_CLASS_ATTRIBUTE_NAME); assertEquals("interfaceName", INTERFACE_NAME_ATTRIBUTE_NAME); assertEquals("group", GROUP_ATTRIBUTE_NAME); assertEquals("version", VERSION_ATTRIBUTE_NAME); - assertEquals(new HashSet(asList("org.apache.dubbo.config.annotation.Service", "com.alibaba.dubbo.config.annotation.Service")), SUPPORTED_ANNOTATION_TYPES); + assertEquals(new LinkedHashSet<>(asList("org.apache.dubbo.config.annotation.DubboService", "org.apache.dubbo.config.annotation.Service", "com.alibaba.dubbo.config.annotation.Service")), SUPPORTED_ANNOTATION_TYPES); } @Test @@ -125,7 +127,7 @@ public class ServiceAnnotationUtilsTest extends AbstractAnnotationProcessingTest @Test public void testGetGroup() { TypeElement type = getType(TestServiceImpl.class); - assertEquals("test",getGroup(getAnnotation(type))); + assertEquals("test", getGroup(getAnnotation(type))); type = getType(GenericTestService.class); assertEquals("generic", getGroup(getAnnotation(type))); diff --git a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/DefaultRestService.java b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/DefaultRestService.java index 2c2245741f..844fe840f3 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/DefaultRestService.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/DefaultRestService.java @@ -16,7 +16,7 @@ */ package org.apache.dubbo.metadata.rest; -import org.apache.dubbo.config.annotation.Service; +import org.apache.dubbo.config.annotation.DubboService; import java.util.Map; @@ -25,7 +25,7 @@ import java.util.Map; * * @since 2.7.6 */ -@Service(version = "1.0.0", group = "default") +@DubboService(version = "1.0.0", group = "default") public class DefaultRestService implements RestService { @Override diff --git a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java index ed4a8a25be..f967ad7c5f 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java @@ -16,7 +16,7 @@ */ package org.apache.dubbo.metadata.rest; -import org.apache.dubbo.config.annotation.Service; +import org.apache.dubbo.config.annotation.DubboService; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; @@ -35,7 +35,7 @@ import java.util.Map; * * @since 2.7.6 */ -@Service(version = "2.0.0", group = "spring") +@DubboService(version = "2.0.0", group = "spring") @RestController public class SpringRestService implements RestService { diff --git a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java index f5d94b7c1e..684d76fd3b 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java @@ -16,7 +16,7 @@ */ package org.apache.dubbo.metadata.rest; -import org.apache.dubbo.config.annotation.Service; +import org.apache.dubbo.config.annotation.DubboService; import javax.ws.rs.Consumes; import javax.ws.rs.FormParam; @@ -34,7 +34,7 @@ import java.util.Map; /** * JAX-RS {@link RestService} */ -@Service(version = "3.0.0", protocol = {"dubbo", "rest"}, group = "standard") +@DubboService(version = "3.0.0", protocol = {"dubbo", "rest"}, group = "standard") @Path("/") public class StandardRestService implements RestService { diff --git a/dubbo-metadata/dubbo-metadata-report-zookeeper/src/test/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReportTest.java b/dubbo-metadata/dubbo-metadata-report-zookeeper/src/test/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReportTest.java index 4e6ae3d82d..27888121ff 100644 --- a/dubbo-metadata/dubbo-metadata-report-zookeeper/src/test/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReportTest.java +++ b/dubbo-metadata/dubbo-metadata-report-zookeeper/src/test/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReportTest.java @@ -1,276 +1,280 @@ -///* -// * 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.store.zookeeper; -// -//import org.apache.dubbo.common.URL; -//import org.apache.dubbo.common.utils.NetUtils; -//import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder; -//import org.apache.dubbo.metadata.definition.model.FullServiceDefinition; -//import org.apache.dubbo.metadata.report.MetadataReport; -//import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum; -//import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier; -//import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier; -//import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier; -// -//import com.google.gson.Gson; -//import org.apache.curator.test.TestingServer; -//import org.junit.jupiter.api.AfterEach; -//import org.junit.jupiter.api.Assertions; -//import org.junit.jupiter.api.BeforeEach; -//import org.junit.jupiter.api.Test; -// -//import java.util.Arrays; -//import java.util.HashMap; -//import java.util.List; -//import java.util.Map; -//import java.util.concurrent.ExecutionException; -// -//import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; -//import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; -// -///** -// * 2018/10/9 -// */ -//public class ZookeeperMetadataReportTest { -// private TestingServer zkServer; -// private MetadataReport zookeeperMetadataReport; -// private URL registryUrl; -// private ZookeeperMetadataReportFactory zookeeperMetadataReportFactory; -// -// @BeforeEach -// public void setUp() throws Exception { -// int zkServerPort = NetUtils.getAvailablePort(); -// this.zkServer = new TestingServer(zkServerPort, true); -// this.registryUrl = URL.valueOf("zookeeper://127.0.0.1:" + zkServerPort); -// -// zookeeperMetadataReportFactory = new ZookeeperMetadataReportFactory(); -// this.zookeeperMetadataReport = zookeeperMetadataReportFactory.getMetadataReport(registryUrl); -// } -// -// @AfterEach -// public void tearDown() throws Exception { -// zkServer.stop(); -// } -// -// private void deletePath(MetadataIdentifier metadataIdentifier, MetadataReport zookeeperMetadataReport) { -// String category = zookeeperMetadataReport.toRootDir() + metadataIdentifier.getUniqueKey(KeyTypeEnum.PATH); -// zookeeperMetadataReport.zkClient.delete(category); -// } -// -// @Test -// public void testStoreProvider() throws ClassNotFoundException, InterruptedException { -// String interfaceName = "org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReport4TstService"; -// String version = "1.0.0.zk.md"; -// String group = null; -// String application = "vic.zk.md"; -// MetadataIdentifier providerMetadataIdentifier = storePrivider(zookeeperMetadataReport, interfaceName, version, group, application); -// -// String fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(providerMetadataIdentifier)); -// fileContent = waitSeconds(fileContent, 3500, zookeeperMetadataReport.getNodePath(providerMetadataIdentifier)); -// Assertions.assertNotNull(fileContent); -// -// deletePath(providerMetadataIdentifier, zookeeperMetadataReport); -// fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(providerMetadataIdentifier)); -// fileContent = waitSeconds(fileContent, 1000, zookeeperMetadataReport.getNodePath(providerMetadataIdentifier)); -// Assertions.assertNull(fileContent); -// -// -// providerMetadataIdentifier = storePrivider(zookeeperMetadataReport, interfaceName, version, group, application); -// fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(providerMetadataIdentifier)); -// fileContent = waitSeconds(fileContent, 3500, zookeeperMetadataReport.getNodePath(providerMetadataIdentifier)); -// Assertions.assertNotNull(fileContent); -// -// Gson gson = new Gson(); -// FullServiceDefinition fullServiceDefinition = gson.fromJson(fileContent, FullServiceDefinition.class); -// Assertions.assertEquals(fullServiceDefinition.getParameters().get("paramTest"), "zkTest"); -// } -// -// -// @Test -// public void testConsumer() throws ClassNotFoundException, InterruptedException { -// String interfaceName = "org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReport4TstService"; -// String version = "1.0.0.zk.md"; -// String group = null; -// String application = "vic.zk.md"; -// MetadataIdentifier consumerMetadataIdentifier = storeConsumer(zookeeperMetadataReport, interfaceName, version, group, application); -// -// String fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(consumerMetadataIdentifier)); -// fileContent = waitSeconds(fileContent, 3500, zookeeperMetadataReport.getNodePath(consumerMetadataIdentifier)); -// Assertions.assertNotNull(fileContent); -// -// deletePath(consumerMetadataIdentifier, zookeeperMetadataReport); -// fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(consumerMetadataIdentifier)); -// fileContent = waitSeconds(fileContent, 1000, zookeeperMetadataReport.getNodePath(consumerMetadataIdentifier)); -// Assertions.assertNull(fileContent); -// -// consumerMetadataIdentifier = storeConsumer(zookeeperMetadataReport, interfaceName, version, group, application); -// fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(consumerMetadataIdentifier)); -// fileContent = waitSeconds(fileContent, 3000, zookeeperMetadataReport.getNodePath(consumerMetadataIdentifier)); -// Assertions.assertNotNull(fileContent); -// Assertions.assertEquals(fileContent, "{\"paramConsumerTest\":\"zkCm\"}"); -// } -// -// @Test -// public void testDoSaveMetadata() throws ExecutionException, InterruptedException { -// String interfaceName = "org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReport4TstService"; -// String version = "1.0.0"; -// String group = null; -// String application = "etc-metadata-report-consumer-test"; -// String revision = "90980"; -// String protocol = "xxx"; -// URL url = generateURL(interfaceName, version, group, application); -// ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(interfaceName, version, -// group, "provider", revision, protocol); -// zookeeperMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url); -// -// String fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(serviceMetadataIdentifier)); -// Assertions.assertNotNull(fileContent); -// -// Assertions.assertEquals(fileContent, URL.encode(url.toFullString())); -// } -// -// @Test -// public void testDoRemoveMetadata() throws ExecutionException, InterruptedException { -// String interfaceName = "org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReport4TstService"; -// String version = "1.0.0"; -// String group = null; -// String application = "etc-metadata-report-consumer-test"; -// String revision = "90980"; -// String protocol = "xxx"; -// URL url = generateURL(interfaceName, version, group, application); -// ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(interfaceName, version, -// group, "provider", revision, protocol); -// zookeeperMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url); -// String fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(serviceMetadataIdentifier)); -// -// Assertions.assertNotNull(fileContent); -// -// -// zookeeperMetadataReport.doRemoveMetadata(serviceMetadataIdentifier); -// -// fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(serviceMetadataIdentifier)); -// Assertions.assertNull(fileContent); -// } -// -// @Test -// public void testDoGetExportedURLs() throws ExecutionException, InterruptedException { -// String interfaceName = "org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReport4TstService"; -// String version = "1.0.0"; -// String group = null; -// String application = "etc-metadata-report-consumer-test"; -// String revision = "90980"; -// String protocol = "xxx"; -// URL url = generateURL(interfaceName, version, group, application); -// ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(interfaceName, version, -// group, "provider", revision, protocol); -// zookeeperMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url); -// -// List r = zookeeperMetadataReport.doGetExportedURLs(serviceMetadataIdentifier); -// Assertions.assertTrue(r.size() == 1); -// -// String fileContent = r.get(0); -// Assertions.assertNotNull(fileContent); -// -// Assertions.assertEquals(fileContent, url.toFullString()); -// } -// -// @Test -// public void testDoSaveSubscriberData() throws ExecutionException, InterruptedException { -// String interfaceName = "org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReport4TstService"; -// String version = "1.0.0"; -// String group = null; -// String application = "etc-metadata-report-consumer-test"; -// String revision = "90980"; -// String protocol = "xxx"; -// URL url = generateURL(interfaceName, version, group, application); -// SubscriberMetadataIdentifier subscriberMetadataIdentifier = new SubscriberMetadataIdentifier(application, revision); -// Gson gson = new Gson(); -// String r = gson.toJson(Arrays.asList(url)); -// zookeeperMetadataReport.doSaveSubscriberData(subscriberMetadataIdentifier, r); -// -// String fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(subscriberMetadataIdentifier)); -// -// Assertions.assertNotNull(fileContent); -// -// Assertions.assertEquals(fileContent, r); -// } -// -// @Test -// public void testDoGetSubscribedURLs() throws ExecutionException, InterruptedException { -// String interfaceName = "org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReport4TstService"; -// String version = "1.0.0"; -// String group = null; -// String application = "etc-metadata-report-consumer-test"; -// String revision = "90980"; -// String protocol = "xxx"; -// URL url = generateURL(interfaceName, version, group, application); -// SubscriberMetadataIdentifier subscriberMetadataIdentifier = new SubscriberMetadataIdentifier(application, revision); -// Gson gson = new Gson(); -// String r = gson.toJson(Arrays.asList(url)); -// zookeeperMetadataReport.doSaveSubscriberData(subscriberMetadataIdentifier, r); -// -// String fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(subscriberMetadataIdentifier)); -// -// Assertions.assertNotNull(fileContent); -// -// Assertions.assertEquals(fileContent, r); -// } -// -// -// private MetadataIdentifier storePrivider(MetadataReport zookeeperMetadataReport, String interfaceName, String version, String group, String application) throws ClassNotFoundException, InterruptedException { -// URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":4444/" + interfaceName + "?paramTest=zkTest&version=" + version + "&application=" -// + application + (group == null ? "" : "&group=" + group)); -// -// MetadataIdentifier providerMetadataIdentifier = new MetadataIdentifier(interfaceName, version, group, PROVIDER_SIDE, application); -// Class interfaceClass = Class.forName(interfaceName); -// FullServiceDefinition fullServiceDefinition = ServiceDefinitionBuilder.buildFullDefinition(interfaceClass, url.getParameters()); -// -// zookeeperMetadataReport.storeProviderMetadata(providerMetadataIdentifier, fullServiceDefinition); -// Thread.sleep(2000); -// return providerMetadataIdentifier; -// } -// -// private MetadataIdentifier storeConsumer(MetadataReport zookeeperMetadataReport, String interfaceName, String version, String group, String application) throws ClassNotFoundException, InterruptedException { -// URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":4444/" + interfaceName + "?version=" + version + "&application=" -// + application + (group == null ? "" : "&group=" + group)); -// -// MetadataIdentifier consumerMetadataIdentifier = new MetadataIdentifier(interfaceName, version, group, CONSUMER_SIDE, application); -// Class interfaceClass = Class.forName(interfaceName); -// -// Map tmp = new HashMap<>(); -// tmp.put("paramConsumerTest", "zkCm"); -// zookeeperMetadataReport.storeConsumerMetadata(consumerMetadataIdentifier, tmp); -// Thread.sleep(2000); -// -// return consumerMetadataIdentifier; -// } -// -// private String waitSeconds(String value, long moreTime, String path) throws InterruptedException { -// if (value == null) { -// Thread.sleep(moreTime); -// return zookeeperMetadataReport.zkClient.getContent(path); -// } -// return value; -// } -// -// private URL generateURL(String interfaceName, String version, String group, String application) { -// URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":8989/" + interfaceName + -// "?paramTest=etcdTest&version=" + version + "&application=" -// + application + (group == null ? "" : "&group=" + group)); -// return url; -// } -//} +/* + * 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.store.zookeeper; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder; +import org.apache.dubbo.metadata.definition.model.FullServiceDefinition; +import org.apache.dubbo.metadata.report.MetadataReport; +import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum; +import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier; +import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier; +import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier; +import org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter; + +import com.google.gson.Gson; +import org.apache.curator.test.TestingServer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; +import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; + +/** + * 2018/10/9 + */ +public class ZookeeperMetadataReportTest { + private TestingServer zkServer; + private ZookeeperMetadataReport zookeeperMetadataReport; + private URL registryUrl; + private ZookeeperMetadataReportFactory zookeeperMetadataReportFactory; + + @BeforeEach + public void setUp() throws Exception { + int zkServerPort = NetUtils.getAvailablePort(); + this.zkServer = new TestingServer(zkServerPort, true); + this.registryUrl = URL.valueOf("zookeeper://127.0.0.1:" + zkServerPort); + + zookeeperMetadataReportFactory = new ZookeeperMetadataReportFactory(); + ZookeeperTransporter zookeeperTransporter = ExtensionLoader.getExtensionLoader(ZookeeperTransporter.class).getExtension("curator"); + zookeeperMetadataReportFactory.setZookeeperTransporter(zookeeperTransporter); + this.zookeeperMetadataReport = (ZookeeperMetadataReport) zookeeperMetadataReportFactory.getMetadataReport(registryUrl); + } + + @AfterEach + public void tearDown() throws Exception { + zkServer.stop(); + } + + private void deletePath(MetadataIdentifier metadataIdentifier, ZookeeperMetadataReport zookeeperMetadataReport) { + String category = zookeeperMetadataReport.toRootDir() + metadataIdentifier.getUniqueKey(KeyTypeEnum.PATH); + zookeeperMetadataReport.zkClient.delete(category); + } + + @Test + public void testStoreProvider() throws ClassNotFoundException, InterruptedException { + String interfaceName = "org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReport4TstService"; + String version = "1.0.0.zk.md"; + String group = null; + String application = "vic.zk.md"; + MetadataIdentifier providerMetadataIdentifier = storePrivider(zookeeperMetadataReport, interfaceName, version, group, application); + + String fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(providerMetadataIdentifier)); + fileContent = waitSeconds(fileContent, 3500, zookeeperMetadataReport.getNodePath(providerMetadataIdentifier)); + Assertions.assertNotNull(fileContent); + + deletePath(providerMetadataIdentifier, zookeeperMetadataReport); + fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(providerMetadataIdentifier)); + fileContent = waitSeconds(fileContent, 1000, zookeeperMetadataReport.getNodePath(providerMetadataIdentifier)); + Assertions.assertNull(fileContent); + + + providerMetadataIdentifier = storePrivider(zookeeperMetadataReport, interfaceName, version, group, application); + fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(providerMetadataIdentifier)); + fileContent = waitSeconds(fileContent, 3500, zookeeperMetadataReport.getNodePath(providerMetadataIdentifier)); + Assertions.assertNotNull(fileContent); + + Gson gson = new Gson(); + FullServiceDefinition fullServiceDefinition = gson.fromJson(fileContent, FullServiceDefinition.class); + Assertions.assertEquals(fullServiceDefinition.getParameters().get("paramTest"), "zkTest"); + } + + + @Test + public void testConsumer() throws ClassNotFoundException, InterruptedException { + String interfaceName = "org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReport4TstService"; + String version = "1.0.0.zk.md"; + String group = null; + String application = "vic.zk.md"; + MetadataIdentifier consumerMetadataIdentifier = storeConsumer(zookeeperMetadataReport, interfaceName, version, group, application); + + String fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(consumerMetadataIdentifier)); + fileContent = waitSeconds(fileContent, 3500, zookeeperMetadataReport.getNodePath(consumerMetadataIdentifier)); + Assertions.assertNotNull(fileContent); + + deletePath(consumerMetadataIdentifier, zookeeperMetadataReport); + fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(consumerMetadataIdentifier)); + fileContent = waitSeconds(fileContent, 1000, zookeeperMetadataReport.getNodePath(consumerMetadataIdentifier)); + Assertions.assertNull(fileContent); + + consumerMetadataIdentifier = storeConsumer(zookeeperMetadataReport, interfaceName, version, group, application); + fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(consumerMetadataIdentifier)); + fileContent = waitSeconds(fileContent, 3000, zookeeperMetadataReport.getNodePath(consumerMetadataIdentifier)); + Assertions.assertNotNull(fileContent); + Assertions.assertEquals(fileContent, "{\"paramConsumerTest\":\"zkCm\"}"); + } + + @Test + public void testDoSaveMetadata() throws ExecutionException, InterruptedException { + String interfaceName = "org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReport4TstService"; + String version = "1.0.0"; + String group = null; + String application = "etc-metadata-report-consumer-test"; + String revision = "90980"; + String protocol = "xxx"; + URL url = generateURL(interfaceName, version, group, application); + ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(interfaceName, version, + group, "provider", revision, protocol); + zookeeperMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url); + + String fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(serviceMetadataIdentifier)); + Assertions.assertNotNull(fileContent); + + Assertions.assertEquals(fileContent, URL.encode(url.toFullString())); + } + + @Test + public void testDoRemoveMetadata() throws ExecutionException, InterruptedException { + String interfaceName = "org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReport4TstService"; + String version = "1.0.0"; + String group = null; + String application = "etc-metadata-report-consumer-test"; + String revision = "90980"; + String protocol = "xxx"; + URL url = generateURL(interfaceName, version, group, application); + ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(interfaceName, version, + group, "provider", revision, protocol); + zookeeperMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url); + String fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(serviceMetadataIdentifier)); + + Assertions.assertNotNull(fileContent); + + + zookeeperMetadataReport.doRemoveMetadata(serviceMetadataIdentifier); + + fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(serviceMetadataIdentifier)); + Assertions.assertNull(fileContent); + } + + @Test + public void testDoGetExportedURLs() throws ExecutionException, InterruptedException { + String interfaceName = "org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReport4TstService"; + String version = "1.0.0"; + String group = null; + String application = "etc-metadata-report-consumer-test"; + String revision = "90980"; + String protocol = "xxx"; + URL url = generateURL(interfaceName, version, group, application); + ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(interfaceName, version, + group, "provider", revision, protocol); + zookeeperMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url); + + List r = zookeeperMetadataReport.doGetExportedURLs(serviceMetadataIdentifier); + Assertions.assertTrue(r.size() == 1); + + String fileContent = r.get(0); + Assertions.assertNotNull(fileContent); + + Assertions.assertEquals(fileContent, url.toFullString()); + } + + @Test + public void testDoSaveSubscriberData() throws ExecutionException, InterruptedException { + String interfaceName = "org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReport4TstService"; + String version = "1.0.0"; + String group = null; + String application = "etc-metadata-report-consumer-test"; + String revision = "90980"; + String protocol = "xxx"; + URL url = generateURL(interfaceName, version, group, application); + SubscriberMetadataIdentifier subscriberMetadataIdentifier = new SubscriberMetadataIdentifier(application, revision); + Gson gson = new Gson(); + String r = gson.toJson(Arrays.asList(url)); + zookeeperMetadataReport.doSaveSubscriberData(subscriberMetadataIdentifier, r); + + String fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(subscriberMetadataIdentifier)); + + Assertions.assertNotNull(fileContent); + + Assertions.assertEquals(fileContent, r); + } + + @Test + public void testDoGetSubscribedURLs() throws ExecutionException, InterruptedException { + String interfaceName = "org.apache.dubbo.metadata.store.zookeeper.ZookeeperMetadataReport4TstService"; + String version = "1.0.0"; + String group = null; + String application = "etc-metadata-report-consumer-test"; + String revision = "90980"; + String protocol = "xxx"; + URL url = generateURL(interfaceName, version, group, application); + SubscriberMetadataIdentifier subscriberMetadataIdentifier = new SubscriberMetadataIdentifier(application, revision); + Gson gson = new Gson(); + String r = gson.toJson(Arrays.asList(url)); + zookeeperMetadataReport.doSaveSubscriberData(subscriberMetadataIdentifier, r); + + String fileContent = zookeeperMetadataReport.zkClient.getContent(zookeeperMetadataReport.getNodePath(subscriberMetadataIdentifier)); + + Assertions.assertNotNull(fileContent); + + Assertions.assertEquals(fileContent, r); + } + + + private MetadataIdentifier storePrivider(MetadataReport zookeeperMetadataReport, String interfaceName, String version, String group, String application) throws ClassNotFoundException, InterruptedException { + URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":4444/" + interfaceName + "?paramTest=zkTest&version=" + version + "&application=" + + application + (group == null ? "" : "&group=" + group)); + + MetadataIdentifier providerMetadataIdentifier = new MetadataIdentifier(interfaceName, version, group, PROVIDER_SIDE, application); + Class interfaceClass = Class.forName(interfaceName); + FullServiceDefinition fullServiceDefinition = ServiceDefinitionBuilder.buildFullDefinition(interfaceClass, url.getParameters()); + + zookeeperMetadataReport.storeProviderMetadata(providerMetadataIdentifier, fullServiceDefinition); + Thread.sleep(2000); + return providerMetadataIdentifier; + } + + private MetadataIdentifier storeConsumer(MetadataReport zookeeperMetadataReport, String interfaceName, String version, String group, String application) throws ClassNotFoundException, InterruptedException { + URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":4444/" + interfaceName + "?version=" + version + "&application=" + + application + (group == null ? "" : "&group=" + group)); + + MetadataIdentifier consumerMetadataIdentifier = new MetadataIdentifier(interfaceName, version, group, CONSUMER_SIDE, application); + Class interfaceClass = Class.forName(interfaceName); + + Map tmp = new HashMap<>(); + tmp.put("paramConsumerTest", "zkCm"); + zookeeperMetadataReport.storeConsumerMetadata(consumerMetadataIdentifier, tmp); + Thread.sleep(2000); + + return consumerMetadataIdentifier; + } + + private String waitSeconds(String value, long moreTime, String path) throws InterruptedException { + if (value == null) { + Thread.sleep(moreTime); + return zookeeperMetadataReport.zkClient.getContent(path); + } + return value; + } + + private URL generateURL(String interfaceName, String version, String group, String application) { + URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":8989/" + interfaceName + + "?paramTest=etcdTest&version=" + version + "&application=" + + application + (group == null ? "" : "&group=" + group)); + return url; + } +} diff --git a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java index be9c521a7c..c9b7fa091f 100644 --- a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java +++ b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java @@ -54,6 +54,7 @@ public class MonitorFilter implements Filter, Filter.Listener { private static final Logger logger = LoggerFactory.getLogger(MonitorFilter.class); private static final String MONITOR_FILTER_START_TIME = "monitor_filter_start_time"; + private static final String MONITOR_REMOTE_HOST_STORE = "monitor_remote_host_store"; /** * The Concurrent counter @@ -82,6 +83,7 @@ public class MonitorFilter implements Filter, Filter.Listener { public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { if (invoker.getUrl().hasAttribute(MONITOR_KEY)) { invocation.put(MONITOR_FILTER_START_TIME, System.currentTimeMillis()); + invocation.put(MONITOR_REMOTE_HOST_STORE, RpcContext.getServiceContext().getRemoteHost()); getConcurrent(invoker, invocation).incrementAndGet(); // count up } return invoker.invoke(invocation); // proceed invocation chain @@ -96,7 +98,7 @@ public class MonitorFilter implements Filter, Filter.Listener { @Override public void onResponse(Result result, Invoker invoker, Invocation invocation) { if (invoker.getUrl().hasAttribute(MONITOR_KEY)) { - collect(invoker, invocation, result, RpcContext.getServiceContext().getRemoteHost(), (long) invocation.get(MONITOR_FILTER_START_TIME), false); + collect(invoker, invocation, result, (String) invocation.get(MONITOR_REMOTE_HOST_STORE), (long) invocation.get(MONITOR_FILTER_START_TIME), false); getConcurrent(invoker, invocation).decrementAndGet(); // count down } } @@ -104,7 +106,7 @@ public class MonitorFilter implements Filter, Filter.Listener { @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { if (invoker.getUrl().hasAttribute(MONITOR_KEY)) { - collect(invoker, invocation, null, RpcContext.getServiceContext().getRemoteHost(), (long) invocation.get(MONITOR_FILTER_START_TIME), true); + collect(invoker, invocation, null, (String) invocation.get(MONITOR_REMOTE_HOST_STORE), (long) invocation.get(MONITOR_FILTER_START_TIME), true); getConcurrent(invoker, invocation).decrementAndGet(); // count down } } diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ListTelnetHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ListTelnetHandler.java index 3da6cf4aa6..02cd3b21c4 100644 --- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ListTelnetHandler.java +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ListTelnetHandler.java @@ -112,6 +112,7 @@ public class ListTelnetHandler implements TelnetHandler { buf.append(" addresses: "); buf.append(ServiceCheckUtils.getConsumerAddressNum(consumer)); } + buf.append("\r\n"); } } diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/LogTelnetHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/LogTelnetHandler.java index c77c86c382..3cbfc8319f 100644 --- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/LogTelnetHandler.java +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/LogTelnetHandler.java @@ -44,7 +44,7 @@ public class LogTelnetHandler implements TelnetHandler { public String telnet(Channel channel, String message) { long size; File file = LoggerFactory.getFile(); - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); if (message == null || message.trim().length() == 0) { buf.append("EXAMPLE: log error / log 100"); } else { diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java index 245b820d7e..836b3aa051 100644 --- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java @@ -16,6 +16,8 @@ */ package org.apache.dubbo.qos.server.handler; +import org.apache.dubbo.common.utils.ExecutorUtil; + import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; @@ -30,7 +32,6 @@ import io.netty.handler.timeout.IdleStateEvent; import io.netty.handler.timeout.IdleStateHandler; import io.netty.util.CharsetUtil; import io.netty.util.concurrent.ScheduledFuture; -import org.apache.dubbo.common.utils.ExecutorUtil; import java.util.List; import java.util.concurrent.TimeUnit; @@ -90,6 +91,7 @@ public class QosProcessHandler extends ByteToMessageDecoder { p.addLast(new StringDecoder(CharsetUtil.UTF_8)); p.addLast(new StringEncoder(CharsetUtil.UTF_8)); p.addLast(new IdleStateHandler(0, 0, 5 * 60)); + p.addLast(new TelnetIdleEventHandler()); p.addLast(new TelnetProcessHandler()); p.remove(this); } diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/TelnetIdleEventHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/TelnetIdleEventHandler.java new file mode 100644 index 0000000000..c12f738d59 --- /dev/null +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/TelnetIdleEventHandler.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.qos.server.handler; + +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.timeout.IdleStateEvent; + +public class TelnetIdleEventHandler extends ChannelDuplexHandler { + private static final Logger log = LoggerFactory.getLogger(TelnetIdleEventHandler.class); + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + // server will close channel when server don't receive any request from client util timeout. + if (evt instanceof IdleStateEvent) { + Channel channel = ctx.channel(); + log.info("IdleStateEvent triggered, close channel " + channel); + channel.close(); + } else { + super.userEventTriggered(ctx, evt); + } + } + +} diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java index 4c2576230a..0328cca158 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java @@ -130,8 +130,8 @@ public class DefaultServiceInstance implements ServiceInstance { return address; } - private static String getAddress(String host, int port) { - return port <= 0 ? host : host + ':' + port; + private static String getAddress(String host, Integer port) { + return port != null && port <= 0 ? host : host + ':' + port; } @Override @@ -223,8 +223,12 @@ public class DefaultServiceInstance implements ServiceInstance { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof DefaultServiceInstance)) return false; + if (this == o) { + return true; + } + if (!(o instanceof DefaultServiceInstance)) { + return false; + } DefaultServiceInstance that = (DefaultServiceInstance) o; boolean equals = Objects.equals(getServiceName(), that.getServiceName()) && Objects.equals(getHost(), that.getHost()) && diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java index 8386d1ba26..02ace2a288 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java @@ -27,9 +27,11 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_APPLICATION_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; public class InstanceAddressURL extends URL { @@ -125,6 +127,8 @@ public class InstanceAddressURL extends URL { return getServiceInterface(); } else if (REMOTE_APPLICATION_KEY.equals(key)) { return instance.getServiceName(); + } else if (SIDE_KEY.equals(key)) { + return CONSUMER_SIDE; } String protocolServiceKey = getProtocolServiceKey(); diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java index 3636c2f4e0..90ca135530 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java @@ -87,7 +87,7 @@ public interface ServiceDiscovery extends Prioritized { /** * Unregisters an instance of {@link ServiceInstance}. * - * @param serviceInstance an instance of {@link ServiceInstance} to be deregistered + * @param serviceInstance an instance of {@link ServiceInstance} to be unregistered * @throws RuntimeException if failed */ void unregister(ServiceInstance serviceInstance) throws RuntimeException; @@ -117,7 +117,7 @@ public interface ServiceDiscovery extends Prioritized { * * @param serviceName the service name * @return non-null {@link List} - * @throws NullPointerException if serviceName is null is null + * @throws NullPointerException if serviceName is null */ default List getInstances(String serviceName) throws NullPointerException { @@ -148,7 +148,7 @@ public interface ServiceDiscovery extends Prioritized { * @param offset the offset of request , the number "0" indicates first page * @param pageSize the number of request, the {@link Integer#MAX_VALUE max value} indicates the range is unlimited * @return non-null {@link Page} object - * @throws NullPointerException if serviceName is null is null + * @throws NullPointerException if serviceName is null * @throws IllegalArgumentException if offset or pageSize is negative number * @throws UnsupportedOperationException if not supported */ @@ -166,7 +166,7 @@ public interface ServiceDiscovery extends Prioritized { * @param pageSize the number of request, the {@link Integer#MAX_VALUE max value} indicates the range is unlimited * @param healthyOnly if true , filter healthy instances only * @return non-null {@link Page} object - * @throws NullPointerException if serviceName is null is null + * @throws NullPointerException if serviceName is null * @throws IllegalArgumentException if offset or pageSize is negative number * @throws UnsupportedOperationException if not supported */ @@ -183,7 +183,7 @@ public interface ServiceDiscovery extends Prioritized { * @param requestSize the number of request, the {@link Integer#MAX_VALUE max value} indicates the range is unlimited * @return non-null read-only {@link Map} whose key is the service name and value is * the {@link Page pagination} of {@link ServiceInstance service instances} - * @throws NullPointerException if serviceName is null is null + * @throws NullPointerException if serviceName is null * @throws IllegalArgumentException if offset or requestSize is negative number * @throws UnsupportedOperationException if not supported */ @@ -199,7 +199,7 @@ public interface ServiceDiscovery extends Prioritized { /** * Add an instance of {@link ServiceInstancesChangedListener} for specified service *

    - * Default, Current method will be invoked by {@link ServiceDiscoveryRegistry#subscribe(URL, NotifyListener) + * Default, current method will be invoked by {@link ServiceDiscoveryRegistry#subscribe(URL, NotifyListener) * the ServiceDiscoveryRegistry on the subscription}, and it's mandatory to * {@link EventDispatcher#addEventListener(EventListener) add} the {@link ServiceInstancesChangedListener} argument * into {@link EventDispatcher} whether the subclass implements same approach or not, thus this method is used to diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java index caa3bee799..a2b0aa832c 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java @@ -24,7 +24,6 @@ import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.registry.AddressListener; -import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.integration.DynamicDirectory; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; @@ -44,7 +43,7 @@ import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE; -public class ServiceDiscoveryRegistryDirectory extends DynamicDirectory implements NotifyListener { +public class ServiceDiscoveryRegistryDirectory extends DynamicDirectory { private static final Logger logger = LoggerFactory.getLogger(ServiceDiscoveryRegistryDirectory.class); // instance address to invoker mapping. diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java index b8bac8e86a..77019cf2df 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java @@ -403,8 +403,12 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ServiceInstancesChangedListener)) return false; + if (this == o) { + return true; + } + if (!(o instanceof ServiceInstancesChangedListener)) { + return false; + } ServiceInstancesChangedListener that = (ServiceInstancesChangedListener) o; return Objects.equals(getServiceNames(), that.getServiceNames()); } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java index 7410404cde..5346e4618d 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java @@ -59,7 +59,6 @@ public class RemoteMetadataServiceImpl { metadataInfos.forEach((registryCluster, metadataInfo) -> { if (!metadataInfo.hasReported()) { SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(serviceName, metadataInfo.calAndGetRevision()); - metadataInfo.calAndGetRevision(); metadataInfo.getExtendParams().put(REGISTRY_CLUSTER_KEY, registryCluster); if (getMetadataReports().size() > 0) { MetadataReport metadataReport = getMetadataReports().get(registryCluster); diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java index f1f5ef383b..be2e0f8e0e 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java @@ -139,9 +139,10 @@ public class MigrationInvoker implements MigrationClusterInvoker { Registry registry = directory.getRegistry(); registry.unregister(directory.getRegisteredConsumerUrl()); directory.unSubscribe(RegistryProtocol.toSubscribeUrl(oldSubscribeUrl)); - registry.register(directory.getRegisteredConsumerUrl()); - - directory.setRegisteredConsumerUrl(newSubscribeUrl); + if (directory.isShouldRegister()) { + registry.register(directory.getRegisteredConsumerUrl()); + directory.setRegisteredConsumerUrl(newSubscribeUrl); + } directory.buildRouterChain(newSubscribeUrl); directory.subscribe(RegistryProtocol.toSubscribeUrl(newSubscribeUrl)); } @@ -453,13 +454,17 @@ public class MigrationInvoker implements MigrationClusterInvoker { } private void clearListener(ClusterInvoker invoker) { - if (invoker == null) return; + if (invoker == null) { + return; + } DynamicDirectory directory = (DynamicDirectory) invoker.getDirectory(); directory.setInvokersChangedListener(null); } private void setListener(ClusterInvoker invoker, InvokersChangedListener listener) { - if (invoker == null) return; + if (invoker == null) { + return; + } DynamicDirectory directory = (DynamicDirectory) invoker.getDirectory(); directory.setInvokersChangedListener(listener); } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java index be2e29e26c..c194b2c4ab 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java @@ -18,7 +18,6 @@ package org.apache.dubbo.registry.client.migration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ConfigurationUtils; -import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; @@ -28,7 +27,6 @@ import org.apache.dubbo.registry.client.migration.model.MigrationStep; import java.util.Set; -@Activate public class MigrationRuleHandler { public static final String DUBBO_SERVICEDISCOVERY_MIGRATION = "dubbo.application.service-discovery.migration"; private static final Logger logger = LoggerFactory.getLogger(MigrationRuleHandler.class); diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java index 8085ed47f9..4543dea260 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java @@ -90,15 +90,18 @@ public abstract class DynamicDirectory extends AbstractDirectory implement public DynamicDirectory(Class serviceType, URL url) { super(url, true); + if (serviceType == null) { throw new IllegalArgumentException("service type is null."); } - shouldRegister = !ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true); - shouldSimplified = url.getParameter(SIMPLIFIED_KEY, false); if (url.getServiceKey() == null || url.getServiceKey().length() == 0) { throw new IllegalArgumentException("registry serviceKey is null."); } + + this.shouldRegister = !ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true); + this.shouldSimplified = url.getParameter(SIMPLIFIED_KEY, false); + this.serviceType = serviceType; this.serviceKey = super.getConsumerUrl().getServiceKey(); diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java index 00fb54a309..d0a40f681b 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java @@ -32,7 +32,6 @@ import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.AddressListener; -import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; @@ -84,7 +83,7 @@ import static org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY; /** * RegistryDirectory */ -public class RegistryDirectory extends DynamicDirectory implements NotifyListener { +public class RegistryDirectory extends DynamicDirectory { private static final Logger logger = LoggerFactory.getLogger(RegistryDirectory.class); private static final ConsumerConfigurationListener CONSUMER_CONFIGURATION_LISTENER = new ConsumerConfigurationListener(); @@ -165,7 +164,8 @@ public class RegistryDirectory extends DynamicDirectory implements NotifyL return ""; } - private void refreshOverrideAndInvoker(List urls) { + // RefreshOverrideAndInvoker will be executed by registryCenter and configCenter, so it should be synchronized. + private synchronized void refreshOverrideAndInvoker(List urls) { // mock zookeeper://xxx?mock=return null overrideDirectoryUrl(); refreshInvoker(urls); @@ -555,14 +555,8 @@ public class RegistryDirectory extends DynamicDirectory implements NotifyL return false; } Map> localUrlInvokerMap = urlInvokerMap; - if (localUrlInvokerMap != null && localUrlInvokerMap.size() > 0) { - for (Map.Entry> entry : localUrlInvokerMap.entrySet()){ - if (entry.getValue().isAvailable()) { - return true; - } - } - } - return false; + return CollectionUtils.isNotEmptyMap(localUrlInvokerMap) + && localUrlInvokerMap.values().stream().anyMatch(Invoker::isAvailable); } /** diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index 6db2cda06a..e238af5347 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -132,7 +132,7 @@ public class RegistryProtocol implements Protocol { APPLICATION_KEY, VERSION_KEY, GROUP_KEY, DUBBO_VERSION_KEY, RELEASE_KEY }; - private final static Logger logger = LoggerFactory.getLogger(InterfaceCompatibleRegistryProtocol.class); + private final static Logger logger = LoggerFactory.getLogger(RegistryProtocol.class); private final Map overrideListeners = new ConcurrentHashMap<>(); private final Map serviceConfigurationListeners = new ConcurrentHashMap<>(); private final ProviderConfigurationListener providerConfigurationListener = new ProviderConfigurationListener(); diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/retry/FailedNotifiedTask.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/retry/FailedNotifiedTask.java deleted file mode 100644 index 43084c6558..0000000000 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/retry/FailedNotifiedTask.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.dubbo.registry.retry; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.timer.Timeout; -import org.apache.dubbo.common.utils.CollectionUtils; -import org.apache.dubbo.registry.NotifyListener; -import org.apache.dubbo.registry.support.FailbackRegistry; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * FailedNotifiedTask - */ -public final class FailedNotifiedTask extends AbstractRetryTask { - - private static final String NAME = "retry notify"; - - private final NotifyListener listener; - - private final List urls = new CopyOnWriteArrayList<>(); - - public FailedNotifiedTask(URL url, NotifyListener listener) { - super(url, null, NAME); - if (listener == null) { - throw new IllegalArgumentException(); - } - this.listener = listener; - } - - public void addUrlToRetry(List urls) { - if (CollectionUtils.isEmpty(urls)) { - return; - } - this.urls.addAll(urls); - } - - public void removeRetryUrl(List urls) { - this.urls.removeAll(urls); - } - - @Override - protected void doRetry(URL url, FailbackRegistry registry, Timeout timeout) { - if (CollectionUtils.isNotEmpty(urls)) { - listener.notify(urls); - urls.clear(); - } - reput(timeout, retryPeriod); - } -} diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistry.java index e5c64f5755..bd0dd595af 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistry.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistry.java @@ -335,6 +335,9 @@ public abstract class AbstractRegistry implements Registry { if (listeners != null) { listeners.remove(listener); } + + // do not forget remove notified + notified.remove(url); } protected void recover() throws Exception { @@ -473,7 +476,7 @@ public abstract class AbstractRegistry implements Registry { } Set destroyRegistered = new HashSet<>(getRegistered()); if (!destroyRegistered.isEmpty()) { - for (URL url : new HashSet<>(getRegistered())) { + for (URL url : new HashSet<>(destroyRegistered)) { if (url.getParameter(DYNAMIC_KEY, true)) { try { unregister(url); diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java index 568f6609e8..188212d8b9 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java @@ -110,13 +110,22 @@ public abstract class AbstractRegistryFactory implements RegistryFactory { } } - @Override - public Registry getRegistry(URL url) { + private Registry getDefaultNopRegistryIfDestroyed() { if (destroyed.get()) { LOGGER.warn("All registry instances have been destroyed, failed to fetch any instance. " + "Usually, this means no need to try to do unnecessary redundant resource clearance, all registries has been taken care of."); return DEFAULT_NOP_REGISTRY; } + return null; + } + + @Override + public Registry getRegistry(URL url) { + + Registry defaultNopRegistry = getDefaultNopRegistryIfDestroyed(); + if (null != defaultNopRegistry) { + return defaultNopRegistry; + } url = URLBuilder.from(url) .setPath(RegistryService.class.getName()) @@ -127,6 +136,13 @@ public abstract class AbstractRegistryFactory implements RegistryFactory { // Lock the registry access process to ensure a single instance of the registry LOCK.lock(); try { + // double check + // fix https://github.com/apache/dubbo/issues/7265. + defaultNopRegistry = getDefaultNopRegistryIfDestroyed(); + if (null != defaultNopRegistry) { + return defaultNopRegistry; + } + Registry registry = REGISTRIES.get(key); if (registry != null) { return registry; diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/FailbackRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/FailbackRegistry.java index cde24bda45..0b5545f2c4 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/FailbackRegistry.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/FailbackRegistry.java @@ -21,7 +21,6 @@ import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.registry.NotifyListener; -import org.apache.dubbo.registry.retry.FailedNotifiedTask; import org.apache.dubbo.registry.retry.FailedRegisteredTask; import org.apache.dubbo.registry.retry.FailedSubscribedTask; import org.apache.dubbo.registry.retry.FailedUnregisteredTask; @@ -56,8 +55,6 @@ public abstract class FailbackRegistry extends AbstractRegistry { private final ConcurrentMap failedUnsubscribed = new ConcurrentHashMap(); - private final ConcurrentMap failedNotified = new ConcurrentHashMap(); - /** * The time in milliseconds the retryExecutor will wait */ @@ -92,11 +89,6 @@ public abstract class FailbackRegistry extends AbstractRegistry { failedUnsubscribed.remove(h); } - public void removeFailedNotifiedTask(URL url, NotifyListener listener) { - Holder h = new Holder(url, listener); - failedNotified.remove(h); - } - private void addFailedRegistered(URL url) { FailedRegisteredTask oldOne = failedRegistered.get(url); if (oldOne != null) { @@ -151,14 +143,13 @@ public abstract class FailbackRegistry extends AbstractRegistry { } } - private void removeFailedSubscribed(URL url, NotifyListener listener) { + public void removeFailedSubscribed(URL url, NotifyListener listener) { Holder h = new Holder(url, listener); FailedSubscribedTask f = failedSubscribed.remove(h); if (f != null) { f.cancel(); } removeFailedUnsubscribed(url, listener); - removeFailedNotified(url, listener); } private void addFailedUnsubscribed(URL url, NotifyListener listener) { @@ -183,28 +174,6 @@ public abstract class FailbackRegistry extends AbstractRegistry { } } - private void addFailedNotified(URL url, NotifyListener listener, List urls) { - Holder h = new Holder(url, listener); - FailedNotifiedTask newTask = new FailedNotifiedTask(url, listener); - FailedNotifiedTask f = failedNotified.putIfAbsent(h, newTask); - if (f == null) { - // never has a retry task. then start a new task for retry. - newTask.addUrlToRetry(urls); - retryTimer.newTimeout(newTask, retryPeriod, TimeUnit.MILLISECONDS); - } else { - // just add urls which needs retry. - newTask.addUrlToRetry(urls); - } - } - - private void removeFailedNotified(URL url, NotifyListener listener) { - Holder h = new Holder(url, listener); - FailedNotifiedTask f = failedNotified.remove(h); - if (f != null) { - f.cancel(); - } - } - ConcurrentMap getFailedRegistered() { return failedRegistered; } @@ -221,9 +190,6 @@ public abstract class FailbackRegistry extends AbstractRegistry { return failedUnsubscribed; } - ConcurrentMap getFailedNotified() { - return failedNotified; - } @Override public void register(URL url) { @@ -396,9 +362,8 @@ public abstract class FailbackRegistry extends AbstractRegistry { try { doNotify(url, listener, urls); } catch (Exception t) { - // Record a failed registration request to a failed list, retry regularly - addFailedNotified(url, listener, urls); - logger.error("Failed to notify for subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t); + // Record a failed registration request to a failed list + logger.error("Failed to notify addresses for subscribe " + url + ", cause: " + t.getMessage(), t); } } @@ -415,6 +380,9 @@ public abstract class FailbackRegistry extends AbstractRegistry { logger.info("Recover register url " + recoverRegistered); } for (URL url : recoverRegistered) { + // remove fail registry or unRegistry task first. + removeFailedRegistered(url); + removeFailedUnregistered(url); addFailedRegistered(url); } } @@ -427,6 +395,8 @@ public abstract class FailbackRegistry extends AbstractRegistry { for (Map.Entry> entry : recoverSubscribed.entrySet()) { URL url = entry.getKey(); for (NotifyListener listener : entry.getValue()) { + // First remove other tasks to ensure that addFailedSubscribed can succeed. + removeFailedSubscribed(url, listener); addFailedSubscribed(url, listener); } } diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/FailbackRegistryTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/FailbackRegistryTest.java index 2687956a6f..94c44a056e 100644 --- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/FailbackRegistryTest.java +++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/FailbackRegistryTest.java @@ -27,7 +27,6 @@ import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import static org.apache.dubbo.registry.Constants.CONSUMER_PROTOCOL; @@ -153,36 +152,6 @@ public class FailbackRegistryTest { assertEquals(true, notified.get()); } - @Test - public void testDoRetry_nofify() throws Exception { - - //Initial value 0 - final AtomicInteger count = new AtomicInteger(0); - - NotifyListener listner = new NotifyListener() { - @Override - public void notify(List urls) { - count.incrementAndGet(); - //The exception is thrown for the first time to see if the back will be called again to incrementAndGet - if (count.get() == 1L) { - throw new RuntimeException("test exception please ignore"); - } - } - }; - registry = new MockRegistry(registryUrl, new CountDownLatch(0)); - registry.subscribe(serviceUrl.setProtocol(CONSUMER_PROTOCOL).addParameters(CollectionUtils.toStringMap("check", "false")), listner); - - assertEquals(1, count.get()); //Make sure that the subscribe call has just been called once count.incrementAndGet after the call is completed - //Wait for the timer. - for (int i = 0; i < trytimes; i++) { - System.out.println("failback notify retry ,times:" + i); - if (count.get() == 2) - break; - Thread.sleep(sleeptime); - } - assertEquals(2, count.get()); - } - @Test public void testRecover() throws Exception { CountDownLatch countDownLatch = new CountDownLatch(4); diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastRegistry.java b/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastRegistry.java index 3a6462d171..7807fecaf8 100644 --- a/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastRegistry.java +++ b/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastRegistry.java @@ -361,9 +361,7 @@ public class MulticastRegistry extends FailbackRegistry { private List toList(Set urls) { List list = new ArrayList(); if (CollectionUtils.isNotEmpty(urls)) { - for (URL url : urls) { - list.add(url); - } + list.addAll(urls); } return list; } diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java index c3d7b2a8ac..bab85a3eaf 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java @@ -236,11 +236,7 @@ public class NacosRegistry extends FailbackRegistry { * @return */ private boolean isServiceNamesWithCompatibleMode(final URL url) { - if (!isAdminProtocol(url) && createServiceName(url).isConcrete()) { - return true; - } else { - return false; - } + return !isAdminProtocol(url) && createServiceName(url).isConcrete(); } @Override @@ -500,19 +496,19 @@ public class NacosRegistry extends FailbackRegistry { } /** - * Notify the Healthy {@link Instance instances} to subscriber. + * Notify the Enabled {@link Instance instances} to subscriber. * * @param url {@link URL} * @param listener {@link NotifyListener} * @param instances all {@link Instance instances} */ private void notifySubscriber(URL url, NotifyListener listener, Collection instances) { - List healthyInstances = new LinkedList<>(instances); - if (healthyInstances.size() > 0) { - // Healthy Instances - filterHealthyInstances(healthyInstances); + List enabledInstances = new LinkedList<>(instances); + if (enabledInstances.size() > 0) { + // Instances + filterEnabledInstances(enabledInstances); } - List urls = toUrlWithEmpty(url, healthyInstances); + List urls = toUrlWithEmpty(url, enabledInstances); NacosRegistry.this.notify(url, listener, urls); } @@ -576,7 +572,7 @@ public class NacosRegistry extends FailbackRegistry { } } - private void filterHealthyInstances(Collection instances) { + private void filterEnabledInstances(Collection instances) { filterData(instances, Instance::isEnabled); } diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistryFactory.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistryFactory.java index 380280ed5b..0b0e59507a 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistryFactory.java +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistryFactory.java @@ -17,10 +17,12 @@ package org.apache.dubbo.registry.nacos; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; import org.apache.dubbo.registry.support.AbstractRegistryFactory; +import static org.apache.dubbo.common.constants.CommonConstants.CONFIG_NAMESPACE_KEY; import static org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils.createNamingService; /** @@ -32,6 +34,12 @@ public class NacosRegistryFactory extends AbstractRegistryFactory { @Override protected String createRegistryCacheKey(URL url) { + String namespace = url.getParameter(CONFIG_NAMESPACE_KEY); + url = URL.valueOf(url.toServiceStringWithoutResolving()); + if (StringUtils.isNotEmpty(namespace)) { + url = url.addParameter(CONFIG_NAMESPACE_KEY, namespace); + } + return url.toFullString(); } diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java index 6dc11c592b..09464b7108 100644 --- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java +++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java @@ -188,10 +188,9 @@ public class ZookeeperRegistry extends CacheableFailbackRegistry { @Override public void doUnsubscribe(URL url, NotifyListener listener) { - super.doUnsubscribe(url, listener); - ConcurrentMap listeners = zkListeners.get(url); + ConcurrentMap listeners = zkListeners.get(url); if (listeners != null) { - org.apache.dubbo.remoting.zookeeper.ChildListener zkListener = listeners.get(listener); + ChildListener zkListener = listeners.remove(listener); if (zkListener != null) { if (ANY_VALUE.equals(url.getServiceInterface())) { String root = toRootPath(); @@ -202,6 +201,10 @@ public class ZookeeperRegistry extends CacheableFailbackRegistry { } } } + + if(listeners.isEmpty()){ + zkListeners.remove(url); + } } } @@ -279,6 +282,7 @@ public class ZookeeperRegistry extends CacheableFailbackRegistry { for (Map.Entry> entry : recoverSubscribed.entrySet()) { URL url = entry.getKey(); for (NotifyListener listener : entry.getValue()) { + removeFailedSubscribed(url, listener); addFailedSubscribed(url, listener); } } diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java index 549b8034c7..14ff81cbdb 100644 --- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java @@ -155,6 +155,16 @@ public class ZookeeperServiceDiscovery extends AbstractServiceDiscovery { serviceInstances.add(serviceInstance); } } + + if (healthyOnly) { + Iterator instanceIterator = serviceInstances.iterator(); + while (instanceIterator.hasNext()) { + ServiceInstance instance = instanceIterator.next(); + if (!instance.isHealthy()) { + instanceIterator.remove(); + } + } + } } catch (KeeperException.NoNodeException e) { logger.warn(p + " path not exist.", e); } diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java index 8cffc3eca2..2e45589fb5 100644 --- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java +++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java @@ -49,7 +49,7 @@ public class ZookeeperServiceDiscoveryChangeWatcher implements CuratorWatcher { private final RegistryNotifier notifier; - private boolean keepWatching = true; + private volatile boolean keepWatching = true; private final String serviceName; diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Request.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Request.java index 2e08e8ffb5..6eea6b02ea 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Request.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Request.java @@ -127,6 +127,16 @@ public class Request { } } + public Request copy() { + Request copy = new Request(mId); + copy.mVersion = this.mVersion; + copy.mTwoWay = this.mTwoWay; + copy.mEvent = this.mEvent; + copy.mBroken = this.mBroken; + copy.mData = this.mData; + return copy; + } + @Override public String toString() { return "Request [id=" + mId + ", version=" + mVersion + ", twoway=" + mTwoWay + ", event=" + mEvent diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java index 1ea6c10951..246bbb2ef8 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java @@ -17,6 +17,7 @@ package org.apache.dubbo.remoting.exchange.codec; import org.apache.dubbo.common.Version; +import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.io.Bytes; import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.common.logger.Logger; @@ -38,6 +39,7 @@ import org.apache.dubbo.remoting.telnet.codec.TelnetCodec; import org.apache.dubbo.remoting.transport.CodecSupport; import org.apache.dubbo.remoting.transport.ExceedPayloadLimitException; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -108,6 +110,14 @@ public class ExchangeCodec extends TelnetCodec { // get data length. int len = Bytes.bytes2int(header, 12); + + // When receiving response, how to exceed the length, then directly construct a response to the client. + // see more detail from https://github.com/apache/dubbo/issues/7021. + Object obj = finishRespWhenOverPayload(channel, len, header); + if (null != obj) { + return obj; + } + checkPayload(channel, len); int tt = len + HEADER_LENGTH; @@ -148,19 +158,22 @@ public class ExchangeCodec extends TelnetCodec { byte status = header[3]; res.setStatus(status); try { - ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto); if (status == Response.OK) { Object data; - if (res.isHeartbeat()) { - data = decodeHeartbeatData(channel, in); - } else if (res.isEvent()) { - data = decodeEventData(channel, in); + if (res.isEvent()) { + byte[] eventPayload = CodecSupport.getPayload(is); + if (CodecSupport.isHeartBeat(eventPayload, proto)) { + // heart beat response data is always null; + data = null; + } else { + data = decodeEventData(channel, CodecSupport.deserialize(channel.getUrl(), new ByteArrayInputStream(eventPayload), proto), eventPayload); + } } else { - data = decodeResponseData(channel, in, getRequestData(id)); + data = decodeResponseData(channel, CodecSupport.deserialize(channel.getUrl(), is, proto), getRequestData(id)); } res.setResult(data); } else { - res.setErrorMessage(in.readUTF()); + res.setErrorMessage(CodecSupport.deserialize(channel.getUrl(), is, proto).readUTF()); } } catch (Throwable t) { res.setStatus(Response.CLIENT_ERROR); @@ -176,14 +189,17 @@ public class ExchangeCodec extends TelnetCodec { req.setEvent(true); } try { - ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto); Object data; - if (req.isHeartbeat()) { - data = decodeHeartbeatData(channel, in); - } else if (req.isEvent()) { - data = decodeEventData(channel, in); + if (req.isEvent()) { + byte[] eventPayload = CodecSupport.getPayload(is); + if (CodecSupport.isHeartBeat(eventPayload, proto)) { + // heart beat response data is always null; + data = null; + } else { + data = decodeEventData(channel, CodecSupport.deserialize(channel.getUrl(), new ByteArrayInputStream(eventPayload), proto), eventPayload); + } } else { - data = decodeRequestData(channel, in); + data = decodeRequestData(channel, CodecSupport.deserialize(channel.getUrl(), is, proto)); } req.setData(data); } catch (Throwable t) { @@ -208,7 +224,7 @@ public class ExchangeCodec extends TelnetCodec { } protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException { - Serialization serialization = getSerialization(channel); + Serialization serialization = getSerialization(channel, req); // header. byte[] header = new byte[HEADER_LENGTH]; // set magic number. @@ -231,16 +247,23 @@ public class ExchangeCodec extends TelnetCodec { int savedWriteIndex = buffer.writerIndex(); buffer.writerIndex(savedWriteIndex + HEADER_LENGTH); ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer); - ObjectOutput out = serialization.serialize(channel.getUrl(), bos); - if (req.isEvent()) { - encodeEventData(channel, out, req.getData()); + + if (req.isHeartbeat()) { + // heartbeat request data is always null + bos.write(CodecSupport.getNullBytesOf(serialization)); } else { - encodeRequestData(channel, out, req.getData(), req.getVersion()); - } - out.flushBuffer(); - if (out instanceof Cleanable) { - ((Cleanable) out).cleanup(); + ObjectOutput out = serialization.serialize(channel.getUrl(), bos); + if (req.isEvent()) { + encodeEventData(channel, out, req.getData()); + } else { + encodeRequestData(channel, out, req.getData(), req.getVersion()); + } + out.flushBuffer(); + if (out instanceof Cleanable) { + ((Cleanable) out).cleanup(); + } } + bos.flush(); bos.close(); int len = bos.writtenBytes(); @@ -256,7 +279,7 @@ public class ExchangeCodec extends TelnetCodec { protected void encodeResponse(Channel channel, ChannelBuffer buffer, Response res) throws IOException { int savedWriteIndex = buffer.writerIndex(); try { - Serialization serialization = getSerialization(channel); + Serialization serialization = getSerialization(channel, res); // header. byte[] header = new byte[HEADER_LENGTH]; // set magic number. @@ -274,21 +297,33 @@ public class ExchangeCodec extends TelnetCodec { buffer.writerIndex(savedWriteIndex + HEADER_LENGTH); ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer); - ObjectOutput out = serialization.serialize(channel.getUrl(), bos); + // encode response data or error message. if (status == Response.OK) { - if (res.isHeartbeat()) { - encodeEventData(channel, out, res.getResult()); - } else { - encodeResponseData(channel, out, res.getResult(), res.getVersion()); + if(res.isHeartbeat()){ + // heartbeat response data is always null + bos.write(CodecSupport.getNullBytesOf(serialization)); + }else { + ObjectOutput out = serialization.serialize(channel.getUrl(), bos); + if (res.isEvent()) { + encodeEventData(channel, out, res.getResult()); + } else { + encodeResponseData(channel, out, res.getResult(), res.getVersion()); + } + out.flushBuffer(); + if (out instanceof Cleanable) { + ((Cleanable) out).cleanup(); + } } } else { + ObjectOutput out = serialization.serialize(channel.getUrl(), bos); out.writeUTF(res.getErrorMessage()); + out.flushBuffer(); + if (out instanceof Cleanable) { + ((Cleanable) out).cleanup(); + } } - out.flushBuffer(); - if (out instanceof Cleanable) { - ((Cleanable) out).cleanup(); - } + bos.flush(); bos.close(); @@ -347,11 +382,6 @@ public class ExchangeCodec extends TelnetCodec { return decodeRequestData(in); } - @Deprecated - protected Object decodeHeartbeatData(ObjectInput in) throws IOException { - return decodeEventData(null, in); - } - protected Object decodeRequestData(ObjectInput in) throws IOException { try { return in.readObject(); @@ -395,19 +425,21 @@ public class ExchangeCodec extends TelnetCodec { return decodeRequestData(channel, in); } - protected Object decodeEventData(Channel channel, ObjectInput in) throws IOException { + protected Object decodeEventData(Channel channel, ObjectInput in, byte[] eventBytes) throws IOException { try { + if (eventBytes != null) { + int dataLen = eventBytes.length; + int threshold = ConfigurationUtils.getSystemConfiguration().getInt("deserialization.event.size", 50); + if (dataLen > threshold) { + throw new IllegalArgumentException("Event data too long, actual size " + threshold + ", threshold " + threshold + " rejected for security consideration."); + } + } return in.readEvent(); } catch (IOException | ClassNotFoundException e) { throw new IOException(StringUtils.toString("Decode dubbo protocol event failed.", e)); } } - @Deprecated - protected Object decodeHeartbeatData(Channel channel, ObjectInput in) throws IOException { - return decodeEventData(channel, in); - } - protected Object decodeRequestData(Channel channel, ObjectInput in) throws IOException { return decodeRequestData(in); } @@ -450,5 +482,26 @@ public class ExchangeCodec extends TelnetCodec { encodeResponseData(out, data); } - + private Object finishRespWhenOverPayload(Channel channel, long size, byte[] header) { + int payload = getPayload(channel); + boolean overPayload = isOverPayload(payload, size); + if (overPayload) { + long reqId = Bytes.bytes2long(header, 4); + byte flag = header[2]; + if ((flag & FLAG_REQUEST) == 0) { + Response res = new Response(reqId); + if ((flag & FLAG_EVENT) != 0) { + res.setEvent(true); + } + // get status. + byte status = header[3]; + res.setStatus(Response.CLIENT_ERROR); + String errorMsg = "Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel; + logger.error(errorMsg); + res.setErrorMessage(errorMsg); + return res; + } + } + return null; + } } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java index e5acf6d54e..beddc2dc58 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java @@ -261,7 +261,7 @@ public class DefaultFuture extends CompletableFuture { } private Request getRequestWithoutData() { - Request newRequest = request; + Request newRequest = request.copy(); newRequest.setData(null); return newRequest; } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/TelnetUtils.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/TelnetUtils.java index 50a06c97f8..219ca04802 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/TelnetUtils.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/TelnetUtils.java @@ -115,7 +115,7 @@ public class TelnetUtils { buf.append("\r\n"); //content for (List row : table) { - StringBuffer rowbuf = new StringBuffer(); + StringBuilder rowbuf = new StringBuilder(); rowbuf.append("|"); for (int j = 0; j < widths.length; j++) { String cell = row.get(j); @@ -125,7 +125,7 @@ public class TelnetUtils { if (rowbuf.length() >= totalWidth) { buf.append(rowbuf.toString()); - rowbuf = new StringBuffer(); + rowbuf = new StringBuilder(); // for(int m = 0;m < maxcountbefore && maxcountbefore < totalWidth ; m++){ // rowbuf.append(" "); // } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/LogTelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/LogTelnetHandler.java index 2d39c23b73..6c0167d3b6 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/LogTelnetHandler.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/LogTelnetHandler.java @@ -44,7 +44,7 @@ public class LogTelnetHandler implements TelnetHandler { public String telnet(Channel channel, String message) { long size = 0; File file = LoggerFactory.getFile(); - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); if (message == null || message.trim().length() == 0) { buf.append("EXAMPLE: log error / log 100"); } else { diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java index 978edd81ba..b64e708765 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java @@ -22,6 +22,7 @@ import org.apache.dubbo.common.status.Status; import org.apache.dubbo.common.status.StatusChecker; import org.apache.dubbo.common.status.support.StatusUtils; import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.telnet.TelnetHandler; import org.apache.dubbo.remoting.telnet.support.Help; @@ -81,7 +82,7 @@ public class StatusTelnetHandler implements TelnetHandler { } String status = channel.getUrl().getParameter("status"); Map statuses = new HashMap(); - if (CollectionUtils.isNotEmptyMap(statuses)) { + if (StringUtils.isNotEmpty(status)) { String[] ss = COMMA_SPLIT_PATTERN.split(status); for (String s : ss) { StatusChecker handler = extensionLoader.getExtension(s); @@ -97,5 +98,4 @@ public class StatusTelnetHandler implements TelnetHandler { Status stat = StatusUtils.getSummaryStatus(statuses); return String.valueOf(stat.getLevel()); } - } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java index 8f6bf61cc8..f4ae30980f 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java @@ -53,8 +53,8 @@ public abstract class AbstractClient extends AbstractEndpoint implements Client public AbstractClient(URL url, ChannelHandler handler) throws RemotingException { super(url, handler); - - needReconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, false); + // set default needReconnect true when channel is not connected + needReconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, true); initExecutor(url); @@ -66,6 +66,7 @@ public abstract class AbstractClient extends AbstractEndpoint implements Client "Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t); } + try { // connect. connect(); @@ -179,27 +180,31 @@ public abstract class AbstractClient extends AbstractEndpoint implements Client } protected void connect() throws RemotingException { - connectLock.lock(); try { - if (isConnected()) { return; } + if (isClosed() || isClosing()) { + logger.warn("No need to connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " + + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + ", cause: client status is closed or closing."); + return; + } + doConnect(); if (!isConnected()) { throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " - + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() - + ", cause: Connect wait timeout: " + getConnectTimeout() + "ms."); + + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + + ", cause: Connect wait timeout: " + getConnectTimeout() + "ms."); } else { if (logger.isInfoEnabled()) { logger.info("Successed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " - + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() - + ", channel is " + this.getChannel()); + + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + + ", channel is " + this.getChannel()); } } @@ -208,8 +213,8 @@ public abstract class AbstractClient extends AbstractEndpoint implements Client } catch (Throwable e) { throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " - + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() - + ", cause: " + e.getMessage(), e); + + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + + ", cause: " + e.getMessage(), e); } finally { connectLock.unlock(); @@ -254,31 +259,46 @@ public abstract class AbstractClient extends AbstractEndpoint implements Client @Override public void close() { - - try { - super.close(); - } catch (Throwable e) { - logger.warn(e.getMessage(), e); + if (isClosed()) { + logger.warn("No need to close connection to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + ", cause: the client status is closed."); + return; } + connectLock.lock(); try { - if (executor != null) { - ExecutorUtil.shutdownNow(executor, 100); + if (isClosed()) { + logger.warn("No need to close connection to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + ", cause: the client status is closed."); + return; } - } catch (Throwable e) { - logger.warn(e.getMessage(), e); - } - try { - disconnect(); - } catch (Throwable e) { - logger.warn(e.getMessage(), e); - } + try { + super.close(); + } catch (Throwable e) { + logger.warn(e.getMessage(), e); + } - try { - doClose(); - } catch (Throwable e) { - logger.warn(e.getMessage(), e); + try { + if (executor != null) { + ExecutorUtil.shutdownNow(executor, 100); + } + } catch (Throwable e) { + logger.warn(e.getMessage(), e); + } + + try { + disconnect(); + } catch (Throwable e) { + logger.warn(e.getMessage(), e); + } + + try { + doClose(); + } catch (Throwable e) { + logger.warn(e.getMessage(), e); + } + + } finally { + connectLock.unlock(); } } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractCodec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractCodec.java index 9be9663710..fd18ed6f99 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractCodec.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractCodec.java @@ -16,9 +16,6 @@ */ package org.apache.dubbo.remoting.transport; -import java.io.IOException; -import java.net.InetSocketAddress; - import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; @@ -27,6 +24,11 @@ import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Codec2; import org.apache.dubbo.remoting.Constants; +import org.apache.dubbo.remoting.exchange.Request; +import org.apache.dubbo.remoting.exchange.Response; + +import java.io.IOException; +import java.net.InetSocketAddress; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; @@ -42,16 +44,37 @@ public abstract class AbstractCodec implements Codec2 { private static final String SERVER_SIDE = "server"; protected static void checkPayload(Channel channel, long size) throws IOException { + int payload = getPayload(channel); + boolean overPayload = isOverPayload(payload, size); + if (overPayload) { + ExceedPayloadLimitException e = new ExceedPayloadLimitException( + "Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel); + logger.error(e); + throw e; + } + } + + protected static int getPayload(Channel channel) { int payload = Constants.DEFAULT_PAYLOAD; if (channel != null && channel.getUrl() != null) { payload = channel.getUrl().getParameter(Constants.PAYLOAD_KEY, Constants.DEFAULT_PAYLOAD); } + return payload; + } + + protected static boolean isOverPayload(int payload, long size) { if (payload > 0 && size > payload) { - ExceedPayloadLimitException e = new ExceedPayloadLimitException( - "Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel); - logger.error(e); - throw e; + return true; } + return false; + } + + protected Serialization getSerialization(Channel channel, Request req) { + return CodecSupport.getSerialization(channel.getUrl()); + } + + protected Serialization getSerialization(Channel channel, Response res) { + return CodecSupport.getSerialization(channel.getUrl()); } protected Serialization getSerialization(Channel channel) { @@ -59,7 +82,7 @@ public abstract class AbstractCodec implements Codec2 { } protected boolean isClientSide(Channel channel) { - String side = (String)channel.getAttribute(SIDE_KEY); + String side = (String) channel.getAttribute(SIDE_KEY); if (CLIENT_SIDE.equals(side)) { return true; } else if (SERVER_SIDE.equals(side)) { diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractEndpoint.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractEndpoint.java index 53ce72fee1..ec8a8825c1 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractEndpoint.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractEndpoint.java @@ -27,9 +27,6 @@ import org.apache.dubbo.remoting.Codec2; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.transport.codec.CodecAdapter; -import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; -import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; - /** * AbstractEndpoint */ @@ -39,14 +36,11 @@ public abstract class AbstractEndpoint extends AbstractPeer implements Resetable private Codec2 codec; - private int timeout; - private int connectTimeout; public AbstractEndpoint(URL url, ChannelHandler handler) { super(url, handler); this.codec = getChannelCodec(url); - this.timeout = url.getPositiveParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT); this.connectTimeout = url.getPositiveParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT); } @@ -66,16 +60,7 @@ public abstract class AbstractEndpoint extends AbstractPeer implements Resetable throw new IllegalStateException("Failed to reset parameters " + url + ", cause: Channel closed. channel: " + getLocalAddress()); } - try { - if (url.hasParameter(TIMEOUT_KEY)) { - int t = url.getParameter(TIMEOUT_KEY, 0); - if (t > 0) { - this.timeout = t; - } - } - } catch (Throwable t) { - logger.error(t.getMessage(), t); - } + try { if (url.hasParameter(Constants.CONNECT_TIMEOUT_KEY)) { int t = url.getParameter(Constants.CONNECT_TIMEOUT_KEY, 0); @@ -86,6 +71,7 @@ public abstract class AbstractEndpoint extends AbstractPeer implements Resetable } catch (Throwable t) { logger.error(t.getMessage(), t); } + try { if (url.hasParameter(Constants.CODEC_KEY)) { this.codec = getChannelCodec(url); @@ -104,10 +90,6 @@ public abstract class AbstractEndpoint extends AbstractPeer implements Resetable return codec; } - protected int getTimeout() { - return timeout; - } - protected int getConnectTimeout() { return connectTimeout; } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java index 967d8d233b..716eaf64b7 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java @@ -37,8 +37,6 @@ import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; import static org.apache.dubbo.remoting.Constants.ACCEPTS_KEY; import static org.apache.dubbo.remoting.Constants.DEFAULT_ACCEPTS; -import static org.apache.dubbo.remoting.Constants.DEFAULT_IDLE_TIMEOUT; -import static org.apache.dubbo.remoting.Constants.IDLE_TIMEOUT_KEY; /** * AbstractServer @@ -51,7 +49,6 @@ public abstract class AbstractServer extends AbstractEndpoint implements Remotin private InetSocketAddress localAddress; private InetSocketAddress bindAddress; private int accepts; - private int idleTimeout; private ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension(); @@ -66,7 +63,6 @@ public abstract class AbstractServer extends AbstractEndpoint implements Remotin } bindAddress = new InetSocketAddress(bindIp, bindPort); this.accepts = url.getParameter(ACCEPTS_KEY, DEFAULT_ACCEPTS); - this.idleTimeout = url.getParameter(IDLE_TIMEOUT_KEY, DEFAULT_IDLE_TIMEOUT); try { doOpen(); if (logger.isInfoEnabled()) { @@ -88,6 +84,7 @@ public abstract class AbstractServer extends AbstractEndpoint implements Remotin if (url == null) { return; } + try { if (url.hasParameter(ACCEPTS_KEY)) { int a = url.getParameter(ACCEPTS_KEY, 0); @@ -98,16 +95,7 @@ public abstract class AbstractServer extends AbstractEndpoint implements Remotin } catch (Throwable t) { logger.error(t.getMessage(), t); } - try { - if (url.hasParameter(IDLE_TIMEOUT_KEY)) { - int t = url.getParameter(IDLE_TIMEOUT_KEY, 0); - if (t > 0) { - this.idleTimeout = t; - } - } - } catch (Throwable t) { - logger.error(t.getMessage(), t); - } + executorRepository.updateThreadpool(url, executor); super.setUrl(getUrl().addParameters(url.getParameters())); } @@ -127,12 +115,15 @@ public abstract class AbstractServer extends AbstractEndpoint implements Remotin if (logger.isInfoEnabled()) { logger.info("Close " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress()); } + ExecutorUtil.shutdownNow(executor, 100); + try { super.close(); } catch (Throwable e) { logger.warn(e.getMessage(), e); } + try { doClose(); } catch (Throwable e) { @@ -159,10 +150,6 @@ public abstract class AbstractServer extends AbstractEndpoint implements Remotin return accepts; } - public int getIdleTimeout() { - return idleTimeout; - } - @Override public void connected(Channel ch) throws RemotingException { // If the server has entered the shutdown process, reject any new connection diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java index 8c74fe564f..d4beb50294 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java @@ -22,25 +22,32 @@ import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.serialize.ObjectOutput; import org.apache.dubbo.common.serialize.Serialization; +import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.Constants; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ProviderModel; +import org.apache.dubbo.rpc.model.ServiceRepository; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; -import static org.apache.dubbo.common.serialize.Constants.COMPACTED_JAVA_SERIALIZATION_ID; -import static org.apache.dubbo.common.serialize.Constants.JAVA_SERIALIZATION_ID; -import static org.apache.dubbo.common.serialize.Constants.NATIVE_JAVA_SERIALIZATION_ID; - public class CodecSupport { - private static final Logger logger = LoggerFactory.getLogger(CodecSupport.class); private static Map ID_SERIALIZATION_MAP = new HashMap(); private static Map ID_SERIALIZATIONNAME_MAP = new HashMap(); private static Map SERIALIZATIONNAME_ID_MAP = new HashMap(); + // Cache null object serialize results, for heartbeat request/response serialize use. + private static Map ID_NULLBYTES_MAP = new HashMap(); + + private static final ThreadLocal TL_BUFFER = ThreadLocal.withInitial(() -> new byte[1024]); static { Set supportedExtensions = ExtensionLoader.getExtensionLoader(Serialization.class).getSupportedExtensions(); @@ -67,7 +74,7 @@ public class CodecSupport { return ID_SERIALIZATION_MAP.get(id); } - public static byte getIDByName(String name) { + public static Byte getIDByName(String name) { return SERIALIZATIONNAME_ID_MAP.get(name); } @@ -77,19 +84,98 @@ public class CodecSupport { } public static Serialization getSerialization(URL url, Byte id) throws IOException { - Serialization serialization = getSerializationById(id); - String serializationName = url.getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION); - // Check if "serialization id" passed from network matches the id on this side(only take effect for JDK serialization), for security purpose. - if (serialization == null - || ((id == JAVA_SERIALIZATION_ID || id == NATIVE_JAVA_SERIALIZATION_ID || id == COMPACTED_JAVA_SERIALIZATION_ID) - && !(serializationName.equals(ID_SERIALIZATIONNAME_MAP.get(id))))) { - throw new IOException("Unexpected serialization id:" + id + " received from network, please check if the peer send the right id."); + Serialization result = getSerializationById(id); + if (result == null) { + throw new IOException("Unrecognized serialize type from consumer: " + id); } - return serialization; + return result; } public static ObjectInput deserialize(URL url, InputStream is, byte proto) throws IOException { Serialization s = getSerialization(url, proto); return s.deserialize(url, is); } + + /** + * Get the null object serialize result byte[] of Serialization from the cache, + * if not, generate it first. + * + * @param s Serialization Instances + * @return serialize result of null object + */ + public static byte[] getNullBytesOf(Serialization s) { + return ID_NULLBYTES_MAP.computeIfAbsent(s.getContentTypeId(), k -> { + //Pre-generated Null object bytes + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] nullBytes = new byte[0]; + try { + ObjectOutput out = s.serialize(null, baos); + out.writeObject(null); + out.flushBuffer(); + nullBytes = baos.toByteArray(); + baos.close(); + } catch (Exception e) { + logger.warn("Serialization extension " + s.getClass().getName() + " not support serializing null object, return an empty bytes instead."); + } + return nullBytes; + }); + } + + /** + * Read all payload to byte[] + * + * @param is + * @return + * @throws IOException + */ + public static byte[] getPayload(InputStream is) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = getBuffer(is.available()); + int len; + while ((len = is.read(buffer)) > -1) { + baos.write(buffer, 0, len); + } + baos.flush(); + return baos.toByteArray(); + } + + private static byte[] getBuffer(int size) { + byte[] bytes = TL_BUFFER.get(); + if (size <= bytes.length) { + return bytes; + } + return new byte[size]; + } + + /** + * Check if payload is null object serialize result byte[] of serialization + * + * @param payload + * @param proto + * @return + */ + public static boolean isHeartBeat(byte[] payload, byte proto) { + return Arrays.equals(payload, getNullBytesOf(getSerializationById(proto))); + } + + public static void checkSerialization(String path, String version, Byte id) throws IOException { + ServiceRepository repository = ApplicationModel.getServiceRepository(); + ProviderModel providerModel = repository.lookupExportedServiceWithoutGroup(path + ":" + version); + if (providerModel == null) { + if (logger.isWarnEnabled()) { + logger.warn("Serialization security check is enabled but cannot work as expected because " + + "there's no matched provider model for path " + path + ", version " + version); + } + } else { + List urls = providerModel.getServiceConfig().getExportedUrls(); + if (CollectionUtils.isNotEmpty(urls)) { + URL url = urls.get(0); + String serializationName = url.getParameter(org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION); + Byte localId = SERIALIZATIONNAME_ID_MAP.get(serializationName); + if (localId != null && !localId.equals(id)) { + throw new IOException("Unexpected serialization id:" + id + " received from network, please check if the peer send the right id."); + } + } + } + } } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/MultiMessageHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/MultiMessageHandler.java index a669802c17..4a173d698b 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/MultiMessageHandler.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/MultiMessageHandler.java @@ -16,17 +16,21 @@ */ package org.apache.dubbo.remoting.transport; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; +import org.apache.dubbo.remoting.ExecutionException; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.support.MultiMessage; /** - * * @see MultiMessage */ public class MultiMessageHandler extends AbstractChannelHandlerDelegate { + protected static final Logger logger = LoggerFactory.getLogger(MultiMessageHandler.class); + public MultiMessageHandler(ChannelHandler handler) { super(handler); } @@ -37,7 +41,12 @@ public class MultiMessageHandler extends AbstractChannelHandlerDelegate { if (message instanceof MultiMessage) { MultiMessage list = (MultiMessage) message; for (Object obj : list) { - handler.received(channel, obj); + try { + handler.received(channel, obj); + } catch (ExecutionException e) { + logger.error("MultiMessageHandler received fail.", e); + handler.caught(channel, e); + } } } else { handler.received(channel, message); diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/MockTransporter.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/MockTransporter.java new file mode 100644 index 0000000000..4bdf890b97 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/MockTransporter.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting; + +import org.apache.dubbo.common.URL; + +import org.mockito.Mockito; + +public class MockTransporter implements Transporter { + private RemotingServer server = Mockito.mock(RemotingServer.class); + private Client client = Mockito.mock(Client.class); + + @Override + public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException { + return server; + } + + @Override + public Client connect(URL url, ChannelHandler handler) throws RemotingException { + return client; + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/TransportersTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/TransportersTest.java new file mode 100644 index 0000000000..8ebe13da12 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/TransportersTest.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting; + +import org.apache.dubbo.common.URL; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TransportersTest { + private String url = "dubbo://127.0.0.1:12345?transporter=mockTransporter"; + private ChannelHandler channel = Mockito.mock(ChannelHandler.class); + + @Test + public void testBind() throws RemotingException { + Assertions.assertThrows(RuntimeException.class, () -> Transporters.bind((String) null)); + Assertions.assertThrows(RuntimeException.class, () -> Transporters.bind((URL) null)); + Assertions.assertThrows(RuntimeException.class, () -> Transporters.bind(url)); + Assertions.assertNotNull(Transporters.bind(url, channel)); + Assertions.assertNotNull(Transporters.bind(url, channel, channel)); + } + + @Test + public void testConnect() throws RemotingException { + Assertions.assertThrows(RuntimeException.class, () -> Transporters.connect((String) null)); + Assertions.assertThrows(RuntimeException.class, () -> Transporters.connect((URL) null)); + Assertions.assertNotNull(Transporters.connect(url)); + Assertions.assertNotNull(Transporters.connect(url, channel)); + Assertions.assertNotNull(Transporters.connect(url, channel, channel)); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java index cb99fe3664..d801c649fc 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java @@ -43,7 +43,6 @@ import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.READONLY_EVENT; -import static org.junit.jupiter.api.Assertions.fail; /** * @@ -96,6 +95,22 @@ public class ExchangeCodecTest extends TelnetCodecTest { return request; } + private byte[] getReadonlyEventRequestBytes(Object obj, byte[] header) throws IOException { + // encode request data. + UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024); + ObjectOutput out = serialization.serialize(url, bos); + out.writeObject(obj); + + out.flushBuffer(); + bos.flush(); + bos.close(); + byte[] data = bos.toByteArray(); +// byte[] len = Bytes.int2bytes(data.length); + System.arraycopy(data, 0, header, 12, data.length); + byte[] request = join(header, data); + return request; + } + private byte[] assemblyDataProtocol(byte[] header) { Person request = new Person(); byte[] newbuf = join(header, objectToByte(request)); @@ -170,12 +185,19 @@ public class ExchangeCodecTest extends TelnetCodecTest { public void test_Decode_Check_Payload() throws IOException { byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; byte[] request = assemblyDataProtocol(header); + try { - testDecode_assertEquals(request, TelnetCodec.DecodeResult.NEED_MORE_INPUT); - fail(); + Channel channel = getServerSideChannel(url); + ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(request); + Object obj = codec.decode(channel, buffer); + + Assertions.assertTrue(obj instanceof Response); + Assertions.assertTrue(((Response) obj).getErrorMessage().startsWith( + "Data length too large: " + Bytes.bytes2int(new byte[]{1, 1, 1, 1}))); } catch (IOException expected) { Assertions.assertTrue(expected.getMessage().startsWith("Data length too large: " + Bytes.bytes2int(new byte[]{1, 1, 1, 1}))); } + } @Test @@ -232,12 +254,14 @@ public class ExchangeCodecTest extends TelnetCodecTest { Person person = new Person(); byte[] request = getRequestBytes(person, header); + System.setProperty("deserialization.event.size", "100"); Request obj = (Request) decode(request); Assertions.assertEquals(person, obj.getData()); Assertions.assertTrue(obj.isTwoWay()); Assertions.assertTrue(obj.isEvent()); Assertions.assertEquals(Version.getProtocolVersion(), obj.getVersion()); System.out.println(obj); + System.clearProperty("deserialization.event.size"); } @Test @@ -271,7 +295,7 @@ public class ExchangeCodecTest extends TelnetCodecTest { @Test public void test_Decode_Return_Request_Object() throws IOException { //|10011111|20-stats=ok|id=0|length=0 - byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) 0xe2, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) 0xc2, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; Person person = new Person(); byte[] request = getRequestBytes(person, header); diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/ExchangersTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/ExchangersTest.java new file mode 100644 index 0000000000..0306d850e1 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/ExchangersTest.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.remoting.exchange; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.remoting.RemotingException; +import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerDispatcher; +import org.apache.dubbo.remoting.exchange.support.Replier; +import org.apache.dubbo.remoting.transport.ChannelHandlerAdapter; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class ExchangersTest { + + @Test + public void testBind() throws RemotingException { + String url = "dubbo://127.0.0.1:12345?exchanger=mockExchanger"; + Exchangers.bind(url, Mockito.mock(Replier.class)); + Exchangers.bind(url, new ChannelHandlerAdapter(), Mockito.mock(Replier.class)); + Exchangers.bind(url, new ExchangeHandlerDispatcher()); + + Assertions.assertThrows(RuntimeException.class, + () -> Exchangers.bind((URL) null, new ExchangeHandlerDispatcher())); + Assertions.assertThrows(RuntimeException.class, + () -> Exchangers.bind(url, (ExchangeHandlerDispatcher) null)); + } + + @Test + public void testConnect() throws RemotingException { + String url = "dubbo://127.0.0.1:12345?exchanger=mockExchanger"; + Exchangers.connect(url); + Exchangers.connect(url, Mockito.mock(Replier.class)); + Exchangers.connect(URL.valueOf(url), Mockito.mock(Replier.class)); + Exchangers.connect(url, new ChannelHandlerAdapter(), Mockito.mock(Replier.class)); + Exchangers.connect(url, new ExchangeHandlerDispatcher()); + + Assertions.assertThrows(RuntimeException.class, + () -> Exchangers.connect((URL) null, new ExchangeHandlerDispatcher())); + Assertions.assertThrows(RuntimeException.class, + () -> Exchangers.connect(url, (ExchangeHandlerDispatcher) null)); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/MockExchanger.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/MockExchanger.java new file mode 100644 index 0000000000..9c820c08cf --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/MockExchanger.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting.exchange; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.remoting.RemotingException; + +import org.mockito.Mockito; + +public class MockExchanger implements Exchanger{ + private ExchangeServer exchangeServer = Mockito.mock(ExchangeServer.class); + private ExchangeClient exchangeClient = Mockito.mock(ExchangeClient.class); + + @Override + public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException { + return exchangeServer; + } + + @Override + public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException { + return exchangeClient; + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java index 6482232b42..67df3b93ac 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java @@ -17,6 +17,10 @@ package org.apache.dubbo.remoting.exchange.support; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.threadpool.ThreadlessExecutor; +import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.TimeoutException; import org.apache.dubbo.remoting.exchange.Request; @@ -28,6 +32,7 @@ import org.junit.jupiter.api.Test; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicInteger; public class DefaultFutureTest { @@ -115,6 +120,43 @@ public class DefaultFutureTest { System.out.println(e.getMessage()); } } + /** + * for example, it will print like this: + *before a future is create , time is : 2021-01-22 10:55:03 + * null + * after a future is timeout , time is : 2021-01-22 10:55:05 + */ + @Test + public void interruptSend() throws Exception { + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + System.out.println("before a future is create , time is : " + LocalDateTime.now().format(formatter)); + // timeout after 1 seconds. + Channel channel = new MockedChannel(); + int channelId = 10; + Request request = new Request(channelId); + ExecutorService sharedExecutor = ExtensionLoader.getExtensionLoader(ExecutorRepository.class) + .getDefaultExtension().createExecutorIfAbsent(URL.valueOf("dubbo://127.0.0.1:23456")); + ThreadlessExecutor executor = new ThreadlessExecutor(sharedExecutor); + DefaultFuture f = DefaultFuture.newFuture(channel, request, 1000, executor); + //mark the future is sent + DefaultFuture.sent(channel, request); + // get operate will throw a interrupted exception, because the thread is interrupted. + try { + new InterruptThread(Thread.currentThread()).start(); + executor.waitAndDrain(); + f.get(); + } catch (Exception e) { + Assertions.assertTrue(e instanceof InterruptedException, "catch exception is not interrupted exception!"); + System.out.println(e.getMessage()); + } + //waiting timeout check task finished + Thread.sleep(1500); + System.out.println("after a future is timeout , time is : " + LocalDateTime.now().format(formatter)); + + DefaultFuture future = DefaultFuture.getFuture(channelId); + //waiting future should be removed by time out check task + Assertions.assertNull(future); + } /** * mock a default future @@ -125,4 +167,29 @@ public class DefaultFutureTest { return DefaultFuture.newFuture(channel, request, timeout, null); } + /** + * mock a thread interrupt another thread which is waiting waitAndDrain() to return. + */ + static class InterruptThread extends Thread { + private Thread parent; + + public InterruptThread(Thread parent) { + this.parent = parent; + } + + @Override + public void run() { + super.run(); + try { + //interrupt waiting thread before timeout + Thread.sleep(500); + parent.interrupt(); + } catch (InterruptedException e) { + System.out.println(e.getMessage()); + } + } + + } + + } diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/TelnetUtilsTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/TelnetUtilsTest.java new file mode 100644 index 0000000000..1822837f34 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/TelnetUtilsTest.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting.telnet; + +import org.apache.dubbo.remoting.telnet.support.TelnetUtils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +public class TelnetUtilsTest { + + /** + * abc - abc - abc + * 1 - 2 - 3 + * x - y - z + */ + @Test + public void testToList() { + List> table = new LinkedList<>(); + table.add(Arrays.asList("abc","abc","abc")); + table.add(Arrays.asList("1","2","3")); + table.add(Arrays.asList("x","y","z")); + + String toList = TelnetUtils.toList(table); + + Assertions.assertTrue(toList.contains("abc - abc - abc")); + Assertions.assertTrue(toList.contains("1 - 2 - 3")); + Assertions.assertTrue(toList.contains("x - y - z")); + } + + /** + * +-----+-----+-----+ + * | A | B | C | + * +-----+-----+-----+ + * | abc | abc | abc | + * | 1 | 2 | 3 | + * | x | y | z | + * +-----+-----+-----+ + */ + @Test + public void testToTable() { + List> table = new LinkedList<>(); + table.add(Arrays.asList("abc","abc","abc")); + table.add(Arrays.asList("1","2","3")); + table.add(Arrays.asList("x","y","z")); + + String toTable = TelnetUtils.toTable(new String[]{"A","B","C"},table); + + Assertions.assertTrue(toTable.contains("| A | B | C |")); + Assertions.assertTrue(toTable.contains("| abc | abc | abc |")); + Assertions.assertTrue(toTable.contains("| 1 | 2 | 3 |")); + Assertions.assertTrue(toTable.contains("| x | y | z |")); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ClearTelnetHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ClearTelnetHandlerTest.java new file mode 100644 index 0000000000..7671ab594b --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ClearTelnetHandlerTest.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting.telnet.support; + +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.telnet.support.command.ClearTelnetHandler; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class ClearTelnetHandlerTest { + + @Test + public void test() { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < 50; i++) { + buf.append("\r\n"); + } + + ClearTelnetHandler telnetHandler = new ClearTelnetHandler(); + Assertions.assertEquals(buf.toString(), telnetHandler.telnet(Mockito.mock(Channel.class), "50")); + + // Illegal Input + Assertions.assertTrue(telnetHandler.telnet(Mockito.mock(Channel.class), "Illegal").contains("Illegal")); + + for (int i = 0; i < 50; i++) { + buf.append("\r\n"); + } + Assertions.assertEquals(buf.toString(), telnetHandler.telnet(Mockito.mock(Channel.class), "")); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ExitTelnetHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ExitTelnetHandlerTest.java new file mode 100644 index 0000000000..470f3106cf --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ExitTelnetHandlerTest.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting.telnet.support; + +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.telnet.support.command.ExitTelnetHandler; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class ExitTelnetHandlerTest { + @Test + public void test() { + Channel channel = Mockito.mock(Channel.class); + + ExitTelnetHandler exitTelnetHandler = new ExitTelnetHandler(); + exitTelnetHandler.telnet(channel, null); + + verify(channel, times(1)).close(); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/HelpTelnetHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/HelpTelnetHandlerTest.java new file mode 100644 index 0000000000..2f2a10e3e5 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/HelpTelnetHandlerTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting.telnet.support; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.telnet.support.command.HelpTelnetHandler; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class HelpTelnetHandlerTest { + @Test + public void test() { + Channel channel = Mockito.mock(Channel.class); + Mockito.when(channel.getUrl()).thenReturn(URL.valueOf("dubbo://127.0.0.1:12345")); + + HelpTelnetHandler helpTelnetHandler = new HelpTelnetHandler(); + // default output + String prompt = "Please input \"help [command]\" show detail.\r\n"; + Assertions.assertTrue(helpTelnetHandler.telnet(channel, "").contains(prompt)); + + // "help" command output + String demoOutput = + "Command:\r\n" + + " help [command]\r\n" + + "Summary:\r\n" + + " Show help.\r\n" + + "Detail:\r\n" + + " Show help."; + Assertions.assertEquals(helpTelnetHandler.telnet(channel, "help"),demoOutput); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/StatusTelnetHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/StatusTelnetHandlerTest.java new file mode 100644 index 0000000000..56e884dca1 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/StatusTelnetHandlerTest.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.remoting.telnet.support; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.telnet.support.command.StatusTelnetHandler; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class StatusTelnetHandlerTest { + @Test + public void test() { + Channel channel = Mockito.mock(Channel.class); + Mockito.when(channel.getUrl()).thenReturn(URL.valueOf("dubbo://127.0.0.1:12345")); + + StatusTelnetHandler statusTelnetHandler = new StatusTelnetHandler(); + Assertions.assertNotNull(statusTelnetHandler.telnet(channel,"")); + Assertions.assertNotNull(statusTelnetHandler.telnet(channel,"-l")); + + String errorPrompt = "Unsupported parameter "; + Assertions.assertTrue(statusTelnetHandler.telnet(channel,"other").contains(errorPrompt)); + + Mockito.when(channel.getUrl()).thenReturn(URL.valueOf("dubbo://127.0.0.1:12345?status=load,memory")); + Assertions.assertNotNull(statusTelnetHandler.telnet(channel,"")); + Assertions.assertNotNull(statusTelnetHandler.telnet(channel,"-l")); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/PayloadDropperTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/PayloadDropperTest.java new file mode 100644 index 0000000000..0e55032006 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/PayloadDropperTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting.utils; + +import org.apache.dubbo.remoting.exchange.Request; +import org.apache.dubbo.remoting.exchange.Response; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class PayloadDropperTest { + @Test + public void test() { + Request request = new Request(1); + request.setData(new Object()); + Request requestWithoutData = (Request) PayloadDropper.getRequestWithoutData(request); + Assertions.assertEquals(requestWithoutData.getId(), request.getId()); + Assertions.assertNull(requestWithoutData.getData()); + + Response response = new Response(1); + response.setResult(new Object()); + Response responseWithoutData = (Response) PayloadDropper.getRequestWithoutData(response); + Assertions.assertEquals(responseWithoutData.getId(), response.getId()); + Assertions.assertNull(responseWithoutData.getResult()); + + Object object = new Object(); + Assertions.assertEquals(object, PayloadDropper.getRequestWithoutData(object)); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/UrlUtilsTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/UrlUtilsTest.java new file mode 100644 index 0000000000..7b475aecc8 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/UrlUtilsTest.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.remoting.utils; + +import org.apache.dubbo.common.URL; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class UrlUtilsTest { + @Test + public void testGetIdleTimeout() { + URL url1 = URL.valueOf("dubbo://127.0.0.1:12345?heartbeat=10000"); + URL url2 = URL.valueOf("dubbo://127.0.0.1:12345?heartbeat=10000&heartbeat.timeout=50000"); + URL url3 = URL.valueOf("dubbo://127.0.0.1:12345?heartbeat=10000&heartbeat.timeout=10000"); + Assertions.assertEquals(UrlUtils.getIdleTimeout(url1), 30000); + Assertions.assertEquals(UrlUtils.getIdleTimeout(url2), 50000); + Assertions.assertThrows(RuntimeException.class, () -> UrlUtils.getIdleTimeout(url3)); + } + + @Test + public void testGetHeartbeat() { + URL url = URL.valueOf("dubbo://127.0.0.1:12345?heartbeat=10000"); + Assertions.assertEquals(UrlUtils.getHeartbeat(url), 10000); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter b/dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter new file mode 100644 index 0000000000..8c02a4a3c1 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter @@ -0,0 +1 @@ +mockTransporter = org.apache.dubbo.remoting.MockTransporter \ No newline at end of file diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger b/dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger new file mode 100644 index 0000000000..f5b1c5687d --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger @@ -0,0 +1 @@ +mockExchanger = org.apache.dubbo.remoting.exchange.MockExchanger \ No newline at end of file diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java index b2f66cab6b..202fd395c9 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java @@ -43,6 +43,7 @@ import java.net.InetSocketAddress; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.apache.dubbo.common.constants.CommonConstants.SSL_ENABLED_KEY; +import static org.apache.dubbo.remoting.Constants.DEFAULT_CONNECT_TIMEOUT; import static org.apache.dubbo.remoting.transport.netty4.NettyEventLoopFactory.eventLoopGroup; import static org.apache.dubbo.remoting.transport.netty4.NettyEventLoopFactory.socketChannelClass; @@ -55,7 +56,7 @@ public class NettyClient extends AbstractClient { /** * netty client bootstrap */ - private static final EventLoopGroup NIO_EVENT_LOOP_GROUP = eventLoopGroup(Constants.DEFAULT_IO_THREADS, "NettyClientWorker"); + private static final EventLoopGroup EVENT_LOOP_GROUP = eventLoopGroup(Constants.DEFAULT_IO_THREADS, "NettyClientWorker"); private static final String SOCKS_PROXY_HOST = "socksProxyHost"; @@ -91,14 +92,14 @@ public class NettyClient extends AbstractClient { protected void doOpen() throws Throwable { final NettyClientHandler nettyClientHandler = new NettyClientHandler(getUrl(), this); bootstrap = new Bootstrap(); - bootstrap.group(NIO_EVENT_LOOP_GROUP) + bootstrap.group(EVENT_LOOP_GROUP) .option(ChannelOption.SO_KEEPALIVE, true) .option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) //.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getTimeout()) .channel(socketChannelClass()); - bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Math.max(3000, getConnectTimeout())); + bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Math.max(DEFAULT_CONNECT_TIMEOUT, getConnectTimeout())); bootstrap.handler(new ChannelInitializer() { @Override diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java index 9d0f067241..46df48e782 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java @@ -46,8 +46,10 @@ import java.util.Map; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.apache.dubbo.common.constants.CommonConstants.IO_THREADS_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.KEEP_ALIVE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SSL_ENABLED_KEY; + /** * NettyServer. */ @@ -94,10 +96,13 @@ public class NettyServer extends AbstractServer implements RemotingServer { final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this); channels = nettyServerHandler.getChannels(); + boolean keepalive = getUrl().getParameter(KEEP_ALIVE_KEY, Boolean.FALSE); + bootstrap.group(bossGroup, workerGroup) .channel(NettyEventLoopFactory.serverSocketChannelClass()) .option(ChannelOption.SO_REUSEADDR, Boolean.TRUE) .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE) + .childOption(ChannelOption.SO_KEEPALIVE, keepalive) .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) .childHandler(new ChannelInitializer() { @Override diff --git a/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClient.java b/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClient.java index a5d45e1831..193943de29 100644 --- a/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClient.java +++ b/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClient.java @@ -43,6 +43,7 @@ import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.Stat; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -57,7 +58,7 @@ public class CuratorZookeeperClient protected static final Logger logger = LoggerFactory.getLogger(CuratorZookeeperClient.class); private static final String ZK_SESSION_EXPIRE_KEY = "zk.session.expire"; - static final Charset CHARSET = Charset.forName("UTF-8"); + static final Charset CHARSET = StandardCharsets.UTF_8; private final CuratorFramework client; private Map treeCacheMap = new ConcurrentHashMap<>(); @@ -402,17 +403,16 @@ public class CuratorZookeeperClient private final long UNKNOWN_SESSION_ID = -1L; private long lastSessionId; - private URL url; + private int timeout; + private int sessionExpireMs; public CuratorConnectionStateListener(URL url) { - this.url = url; + this.timeout = url.getParameter(TIMEOUT_KEY, DEFAULT_CONNECTION_TIMEOUT_MS); + this.sessionExpireMs = url.getParameter(ZK_SESSION_EXPIRE_KEY, DEFAULT_SESSION_TIMEOUT_MS); } @Override public void stateChanged(CuratorFramework client, ConnectionState state) { - int timeout = url.getParameter(TIMEOUT_KEY, DEFAULT_CONNECTION_TIMEOUT_MS); - int sessionExpireMs = url.getParameter(ZK_SESSION_EXPIRE_KEY, DEFAULT_SESSION_TIMEOUT_MS); - long sessionId = UNKNOWN_SESSION_ID; try { sessionId = client.getZookeeperClient().getZooKeeper().getSessionId(); diff --git a/dubbo-rpc/dubbo-rpc-api/pom.xml b/dubbo-rpc/dubbo-rpc-api/pom.xml index f42908eb59..4a8fd42b48 100644 --- a/dubbo-rpc/dubbo-rpc-api/pom.xml +++ b/dubbo-rpc/dubbo-rpc-api/pom.xml @@ -50,5 +50,11 @@ hessian-lite test + + + com.google.code.gson + gson + + \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java index 7ebff3fb4e..118e70151d 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java @@ -16,7 +16,8 @@ */ package org.apache.dubbo.rpc; -import java.lang.reflect.Field; +import org.apache.dubbo.rpc.proxy.InvokerInvocationHandler; + import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -26,13 +27,15 @@ import java.util.concurrent.TimeoutException; import java.util.function.BiConsumer; import java.util.function.Function; +import static org.apache.dubbo.rpc.Constants.INVOCATION_KEY; + /** * {@link AsyncRpcResult} is introduced in 3.0.0 to replace RpcResult, and RpcResult is replaced with {@link AppResponse}: *
      *
    • AsyncRpcResult is the object that is actually passed in the call chain
    • *
    • AppResponse only simply represents the business result
    • *
    - * + *

    * The relationship between them can be described as follow, an abstraction of the definition of AsyncRpcResult: *

      *  {@code
    @@ -55,9 +58,15 @@ public class AppResponse implements Result {
     
         private Map attachments = new HashMap<>();
     
    +    private Map attributes = new HashMap<>();
    +
         public AppResponse() {
         }
     
    +    public AppResponse(Invocation invocation) {
    +        this.setAttribute(INVOCATION_KEY, invocation);
    +    }
    +
         public AppResponse(Object result) {
             this.result = result;
         }
    @@ -71,15 +80,7 @@ public class AppResponse implements Result {
             if (exception != null) {
                 // fix issue#619
                 try {
    -                // get Throwable class
    -                Class clazz = exception.getClass();
    -                while (!clazz.getName().equals(Throwable.class.getName())) {
    -                    clazz = clazz.getSuperclass();
    -                }
    -                // get stackTrace value
    -                Field stackTraceField = clazz.getDeclaredField("stackTrace");
    -                stackTraceField.setAccessible(true);
    -                Object stackTrace = stackTraceField.get(exception);
    +                Object stackTrace = InvokerInvocationHandler.stackTraceField.get(exception);
                     if (stackTrace == null) {
                         exception.setStackTrace(new StackTraceElement[0]);
                     }
    @@ -212,6 +213,14 @@ public class AppResponse implements Result {
             attachments.put(key, value);
         }
     
    +    public Object getAttribute(String key) {
    +        return attributes.get(key);
    +    }
    +
    +    public void setAttribute(String key, Object value) {
    +        attributes.put(key, value);
    +    }
    +
         @Override
         public Result whenCompleteWithContext(BiConsumer fn) {
             throw new UnsupportedOperationException("AppResponse represents an concrete business response, there will be no status changes, you should get internal values directly.");
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java
    index 1e6ea783c8..1feeeade87 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java
    @@ -94,7 +94,7 @@ public class AsyncRpcResult implements Result {
                 if (responseFuture.isDone()) {
                     responseFuture.get().setValue(value);
                 } else {
    -                AppResponse appResponse = new AppResponse();
    +                AppResponse appResponse = new AppResponse(invocation);
                     appResponse.setValue(value);
                     responseFuture.complete(appResponse);
                 }
    @@ -116,7 +116,7 @@ public class AsyncRpcResult implements Result {
                 if (responseFuture.isDone()) {
                     responseFuture.get().setException(t);
                 } else {
    -                AppResponse appResponse = new AppResponse();
    +                AppResponse appResponse = new AppResponse(invocation);
                     appResponse.setException(t);
                     responseFuture.complete(appResponse);
                 }
    @@ -319,7 +319,7 @@ public class AsyncRpcResult implements Result {
     
         public static AsyncRpcResult newDefaultAsyncResult(Object value, Throwable t, Invocation invocation) {
             CompletableFuture future = new CompletableFuture<>();
    -        AppResponse result = new AppResponse();
    +        AppResponse result = new AppResponse(invocation);
             if (t != null) {
                 result.setException(t);
             } else {
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AttachmentsAdapter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AttachmentsAdapter.java
    index 1703792cab..def349a753 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AttachmentsAdapter.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AttachmentsAdapter.java
    @@ -30,9 +30,9 @@ public class AttachmentsAdapter {
     
             public ObjectToStringMap(Map attachments) {
                 for (Entry entry : attachments.entrySet()) {
    -                String converResult = convert(entry.getValue());
    -                if (converResult != null) {
    -                    super.put(entry.getKey(), converResult);
    +                String convertResult = convert(entry.getValue());
    +                if (convertResult != null) {
    +                    super.put(entry.getKey(), convertResult);
                     }
                 }
                 this.attachments = attachments;
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Constants.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Constants.java
    index f5fd98278b..a544fa44e0 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Constants.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Constants.java
    @@ -90,4 +90,8 @@ public interface Constants {
     
         String CONSUMER_MODEL = "consumerModel";
         String METHOD_MODEL = "methodModel";
    +
    +    String SERIALIZATION_SECURITY_CHECK_KEY = "serialization.security.check";
    +    String INVOCATION_KEY = "invocation";
    +    String SERIALIZATION_ID_KEY = "serialization_id";
     }
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java
    index e5b9997f60..4dccbabd9c 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java
    @@ -126,7 +126,6 @@ public class RpcInvocation implements Invocation, Serializable {
     
         public RpcInvocation(Method method, String serviceName, String protocolServiceKey, Object[] arguments, Map attachment, Map attributes) {
             this(method.getName(), serviceName, protocolServiceKey, method.getParameterTypes(), arguments, attachment, null, attributes);
    -        this.returnType = method.getReturnType();
         }
     
         public RpcInvocation(String methodName, String serviceName, String protocolServiceKey, Class[] parameterTypes, Object[] arguments) {
    @@ -160,6 +159,7 @@ public class RpcInvocation implements Invocation, Serializable {
                         this.parameterTypesDesc = methodDescriptor.getParamDesc();
                         this.compatibleParamSignatures = methodDescriptor.getCompatibleParamSignatures();
                         this.returnTypes = methodDescriptor.getReturnTypes();
    +                    this.returnType = methodDescriptor.getReturnClass();
                     }
                 }
             }
    @@ -168,6 +168,7 @@ public class RpcInvocation implements Invocation, Serializable {
                 this.parameterTypesDesc = ReflectUtils.getDesc(this.getParameterTypes());
                 this.compatibleParamSignatures = Stream.of(this.parameterTypes).map(Class::getName).toArray(String[]::new);
                 this.returnTypes = RpcUtils.getReturnTypes(this);
    +            this.returnType = RpcUtils.getReturnType(this);
             }
         }
     
    @@ -180,10 +181,12 @@ public class RpcInvocation implements Invocation, Serializable {
             this.invoker = invoker;
         }
     
    +    @Override
         public Object put(Object key, Object value) {
             return attributes.put(key, value);
         }
     
    +    @Override
         public Object get(Object key) {
             return attributes.get(key);
         }
    @@ -242,6 +245,7 @@ public class RpcInvocation implements Invocation, Serializable {
             this.parameterTypesDesc = parameterTypesDesc;
         }
     
    +    @Override
         public String[] getCompatibleParamSignatures() {
             return compatibleParamSignatures;
         }
    @@ -286,6 +290,7 @@ public class RpcInvocation implements Invocation, Serializable {
             this.attachments = attachments == null ? new LinkedHashMap<>() : new LinkedHashMap<>(attachments);
         }
     
    +    @Override
         public void setAttachment(String key, Object value) {
             setObjectAttachment(key, value);
         }
    @@ -303,6 +308,7 @@ public class RpcInvocation implements Invocation, Serializable {
             setObjectAttachmentIfAbsent(key, value);
         }
     
    +    @Override
         public void setAttachmentIfAbsent(String key, Object value) {
             setObjectAttachmentIfAbsent(key, value);
         }
    @@ -401,6 +407,7 @@ public class RpcInvocation implements Invocation, Serializable {
         }
     
         @Deprecated
    +    @Override
         public Object getObjectAttachment(String key, Object defaultValue) {
             if (attachments == null) {
                 return defaultValue;
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcStatus.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcStatus.java
    index 3333f0ff93..093baf581d 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcStatus.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcStatus.java
    @@ -32,10 +32,14 @@ import java.util.concurrent.atomic.AtomicLong;
      */
     public class RpcStatus {
     
    -    private static final ConcurrentMap SERVICE_STATISTICS = new ConcurrentHashMap();
    +    private static final ConcurrentMap SERVICE_STATISTICS = new ConcurrentHashMap();
    +
    +    private static final ConcurrentMap> METHOD_STATISTICS =
    +            new ConcurrentHashMap>();
     
    -    private static final ConcurrentMap> METHOD_STATISTICS = new ConcurrentHashMap>();
         private final ConcurrentMap values = new ConcurrentHashMap();
    +
         private final AtomicInteger active = new AtomicInteger();
         private final AtomicLong total = new AtomicLong();
         private final AtomicInteger failed = new AtomicInteger();
    @@ -103,14 +107,18 @@ public class RpcStatus {
             }
             for (int i; ; ) {
                 i = methodStatus.active.get();
    -            if (i + 1 > max) {
    +
    +            if (i == Integer.MAX_VALUE || i + 1 > max) {
                     return false;
                 }
    +
                 if (methodStatus.active.compareAndSet(i, i + 1)) {
                     break;
                 }
             }
    +
             appStatus.active.incrementAndGet();
    +
             return true;
         }
     
    @@ -128,13 +136,16 @@ public class RpcStatus {
             status.active.decrementAndGet();
             status.total.incrementAndGet();
             status.totalElapsed.addAndGet(elapsed);
    +
             if (status.maxElapsed.get() < elapsed) {
                 status.maxElapsed.set(elapsed);
             }
    +
             if (succeeded) {
                 if (status.succeededMaxElapsed.get() < elapsed) {
                     status.succeededMaxElapsed.set(elapsed);
                 }
    +
             } else {
                 status.failed.incrementAndGet();
                 status.failedElapsed.addAndGet(elapsed);
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java
    index 1cf09fbbf0..653a4fcd3a 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java
    @@ -101,7 +101,8 @@ public class AccessLogFilter implements Filter {
             try {
                 String accessLogKey = invoker.getUrl().getParameter(ACCESS_LOG_KEY);
                 if (ConfigUtils.isNotEmpty(accessLogKey)) {
    -                AccessLogData logData = buildAccessLogData(invoker, inv);
    +                AccessLogData logData = AccessLogData.newLogData(); 
    +                logData.buildAccessLogData(invoker, inv);
                     log(accessLogKey, logData);
                 }
             } catch (Throwable t) {
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java
    index c12f016e50..28e534d06c 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java
    @@ -65,8 +65,8 @@ public class ExceptionFilter implements Filter, Filter.Listener {
                     // directly throw if the exception appears in the signature
                     try {
                         Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
    -                    Class[] exceptionClassses = method.getExceptionTypes();
    -                    for (Class exceptionClass : exceptionClassses) {
    +                    Class[] exceptionClasses = method.getExceptionTypes();
    +                    for (Class exceptionClass : exceptionClasses) {
                             if (exception.getClass().equals(exceptionClass)) {
                                 return;
                             }
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java
    index cb37de4f49..41bc2d1471 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java
    @@ -43,12 +43,19 @@ import org.apache.dubbo.rpc.service.GenericException;
     import org.apache.dubbo.rpc.service.GenericService;
     import org.apache.dubbo.rpc.support.ProtocolUtils;
     
    +import com.google.gson.Gson;
    +import com.google.gson.JsonSyntaxException;
    +import com.google.gson.reflect.TypeToken;
    +
     import java.io.IOException;
     import java.lang.reflect.Method;
    +import java.lang.reflect.Type;
    +import java.util.stream.IntStream;
     
     import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE;
     import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE_ASYNC;
     import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_BEAN;
    +import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_GSON;
     import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_NATIVE_JAVA;
     import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_PROTOBUF;
     import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
    @@ -76,8 +83,13 @@ public class GenericFilter implements Filter, Filter.Listener {
                         args = new Object[params.length];
                     }
     
    +                if(types == null) {
    +                    types = new String[params.length];
    +                }
    +
                     if (args.length != types.length) {
    -                    throw new RpcException("args.length != types.length");
    +                    throw new RpcException("GenericFilter#invoke args.length != types.length, please check your "
    +                            + "params");
                     }
                     String generic = inv.getAttachment(GENERIC_KEY);
     
    @@ -88,7 +100,13 @@ public class GenericFilter implements Filter, Filter.Listener {
                     if (StringUtils.isEmpty(generic)
                             || ProtocolUtils.isDefaultGenericSerialization(generic)
                             || ProtocolUtils.isGenericReturnRawResult(generic)) {
    -                    args = PojoUtils.realize(args, params, method.getGenericParameterTypes());
    +                    try {
    +                        args = PojoUtils.realize(args, params, method.getGenericParameterTypes());
    +                    } catch (IllegalArgumentException e) {
    +                        throw new RpcException(e);
    +                    }
    +                } else if (ProtocolUtils.isGsonGenericSerialization(generic)) {
    +                    args = getGsonGenericArgs(args, method.getGenericParameterTypes());
                     } else if (ProtocolUtils.isJavaGenericSerialization(generic)) {
                         Configuration configuration = ApplicationModel.getEnvironment().getConfiguration();
                         if (!configuration.getBoolean(CommonConstants.ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE, false)) {
    @@ -157,7 +175,9 @@ public class GenericFilter implements Filter, Filter.Listener {
                         }
                     }
     
    -                RpcInvocation rpcInvocation = new RpcInvocation(method, invoker.getInterface().getName(), invoker.getUrl().getProtocolServiceKey(), args, inv.getObjectAttachments(), inv.getAttributes());
    +                RpcInvocation rpcInvocation =
    +                        new RpcInvocation(method, invoker.getInterface().getName(), invoker.getUrl().getProtocolServiceKey(), args,
    +                                inv.getObjectAttachments(), inv.getAttributes());
                     rpcInvocation.setInvoker(inv.getInvoker());
                     rpcInvocation.setTargetServiceUniqueName(inv.getTargetServiceUniqueName());
     
    @@ -169,6 +189,19 @@ public class GenericFilter implements Filter, Filter.Listener {
             return invoker.invoke(inv);
         }
     
    +    private Object[] getGsonGenericArgs(final Object[] args, Type[] types) {
    +        Gson gson = new Gson();
    +        return IntStream.range(0, args.length).mapToObj(i -> {
    +            String str = args[i].toString();
    +            Type type = TypeToken.get(types[i]).getType();
    +            try {
    +                return gson.fromJson(str, type);
    +            } catch (JsonSyntaxException ex) {
    +                throw new RpcException(String.format("Generic serialization [%s] Json syntax exception thrown when parsing (message:%s type:%s) error:%s", GENERIC_SERIALIZATION_GSON, str, type.toString(), ex.getMessage()));
    +            }
    +        }).toArray();
    +    }
    +
         @Override
         public void onResponse(Result appResponse, Invoker invoker, Invocation inv) {
             if ((inv.getMethodName().equals($INVOKE) || inv.getMethodName().equals($INVOKE_ASYNC))
    @@ -195,7 +228,8 @@ public class GenericFilter implements Filter, Filter.Listener {
                 if (ProtocolUtils.isJavaGenericSerialization(generic)) {
                     try {
                         UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512);
    -                    ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(GENERIC_SERIALIZATION_NATIVE_JAVA).serialize(null, os).writeObject(appResponse.getValue());
    +                    ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(GENERIC_SERIALIZATION_NATIVE_JAVA)
    +                            .serialize(null, os).writeObject(appResponse.getValue());
                         appResponse.setValue(os.toByteArray());
                     } catch (IOException e) {
                         throw new RpcException(
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericImplFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericImplFilter.java
    index 31f6b50768..b76caeae6e 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericImplFilter.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericImplFilter.java
    @@ -107,7 +107,7 @@ public class GenericImplFilter implements Filter, Filter.Listener {
                 if (ProtocolUtils.isJavaGenericSerialization(generic)) {
     
                     for (Object arg : args) {
    -                    if (!(byte[].class == arg.getClass())) {
    +                    if (byte[].class != arg.getClass()) {
                             error(generic, byte[].class.getName(), arg.getClass().getName());
                         }
                     }
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenFilter.java
    index 58ada5c96f..f3ed745eef 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenFilter.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenFilter.java
    @@ -48,7 +48,9 @@ public class TokenFilter implements Filter {
                 Map attachments = inv.getObjectAttachments();
                 String remoteToken = (attachments == null ? null : (String) attachments.get(TOKEN_KEY));
                 if (!token.equals(remoteToken)) {
    -                throw new RpcException("Invalid token! Forbid invoke remote service " + serviceType + " method " + inv.getMethodName() + "() from consumer " + RpcContext.getServiceContext().getRemoteHost() + " to provider " + RpcContext.getServiceContext().getLocalHost());
    +                throw new RpcException("Invalid token! Forbid invoke remote service " + serviceType + " method " + inv.getMethodName() +
    +                        "() from consumer " + RpcContext.getServiceContext().getRemoteHost() + " to provider " +
    +                        RpcContext.getServiceContext().getLocalHost()+ ", consumer incorrect token is " + remoteToken);
                 }
             }
             return invoker.invoke(inv);
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/tps/StatItem.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/tps/StatItem.java
    index 9fb3fab7d9..348b9ccef5 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/tps/StatItem.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/tps/StatItem.java
    @@ -49,7 +49,7 @@ class StatItem {
                 lastResetTime = now;
             }
     
    -        if (token.sum() < 0) {
    +        if (token.sum() <= 0) {
                 return false;
             }
             token.decrement();
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractExporter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractExporter.java
    index 41a47806c9..99c9ebd2ff 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractExporter.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractExporter.java
    @@ -51,12 +51,19 @@ public abstract class AbstractExporter implements Exporter {
         }
     
         @Override
    -    public void unexport() {
    +    final public void unexport() {
             if (unexported) {
                 return;
             }
             unexported = true;
             getInvoker().destroy();
    +        afterUnExport();
    +    }
    +
    +    /**
    +     * subclasses need to override this method to destroy resources.
    +     */
    +    public void afterUnExport() {
         }
     
         @Override
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
    index 5904da3fc7..c75f107741 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
    @@ -28,6 +28,7 @@ import org.apache.dubbo.common.utils.CollectionUtils;
     import org.apache.dubbo.common.utils.NetUtils;
     import org.apache.dubbo.remoting.RemotingException;
     import org.apache.dubbo.remoting.TimeoutException;
    +import org.apache.dubbo.remoting.transport.CodecSupport;
     import org.apache.dubbo.rpc.AsyncRpcResult;
     import org.apache.dubbo.rpc.Invocation;
     import org.apache.dubbo.rpc.InvokeMode;
    @@ -49,6 +50,10 @@ import java.util.concurrent.ExecutionException;
     import java.util.concurrent.ExecutorService;
     import java.util.concurrent.TimeUnit;
     
    +import static org.apache.dubbo.remoting.Constants.DEFAULT_REMOTING_SERIALIZATION;
    +import static org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY;
    +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_ID_KEY;
    +
     /**
      * This Invoker works on Consumer side.
      */
    @@ -168,6 +173,12 @@ public abstract class AbstractInvoker implements Invoker {
             invocation.setInvokeMode(RpcUtils.getInvokeMode(url, invocation));
             RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
     
    +        Byte serializationId = CodecSupport.getIDByName(getUrl().getParameter(SERIALIZATION_KEY, DEFAULT_REMOTING_SERIALIZATION));
    +        if (serializationId != null) {
    +            invocation.put(SERIALIZATION_ID_KEY, serializationId);
    +        }
    +
    +
             AsyncRpcResult asyncResult;
             try {
                 asyncResult = (AsyncRpcResult) doInvoke(invocation);
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java
    index 8ee51ecd49..c9965a2bbc 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java
    @@ -87,8 +87,7 @@ public abstract class AbstractProxyProtocol extends AbstractProtocol {
             final Runnable runnable = doExport(proxyFactory.getProxy(invoker, true), invoker.getInterface(), invoker.getUrl());
             exporter = new AbstractExporter(invoker) {
                 @Override
    -            public void unexport() {
    -                super.unexport();
    +            public void afterUnExport() {
                     exporterMap.remove(uri);
                     if (runnable != null) {
                         try {
    @@ -130,6 +129,13 @@ public abstract class AbstractProxyProtocol extends AbstractProtocol {
                         throw getRpcException(type, url, invocation, e);
                     }
                 }
    +
    +            @Override
    +            public void destroy() {
    +                super.destroy();
    +                target.destroy();
    +                invokers.remove(this);
    +            }
             };
             invokers.add(invoker);
             return invoker;
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyFactory.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyFactory.java
    index ab1ab4443b..5a00daabe8 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyFactory.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyFactory.java
    @@ -22,10 +22,9 @@ import org.apache.dubbo.rpc.Invoker;
     import org.apache.dubbo.rpc.ProxyFactory;
     import org.apache.dubbo.rpc.RpcException;
     import org.apache.dubbo.rpc.service.Destroyable;
    +import org.apache.dubbo.rpc.service.EchoService;
     import org.apache.dubbo.rpc.service.GenericService;
     
    -import com.alibaba.dubbo.rpc.service.EchoService;
    -
     import java.util.Arrays;
     import java.util.HashSet;
     import java.util.Set;
    @@ -60,7 +59,7 @@ public abstract class AbstractProxyFactory implements ProxyFactory {
             }
     
             if (generic) {
    -            if (!GenericService.class.isAssignableFrom(invoker.getInterface())) {
    +            if (GenericService.class.equals(invoker.getInterface()) || !GenericService.class.isAssignableFrom(invoker.getInterface())) {
                     interfaces.add(com.alibaba.dubbo.rpc.service.GenericService.class);
                 }
     
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java
    index 25c7ee9015..67feea895b 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java
    @@ -82,9 +82,9 @@ public abstract class AbstractProxyInvoker implements Invoker {
         public Result invoke(Invocation invocation) throws RpcException {
             try {
                 Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
    -			CompletableFuture future = wrapWithFuture(value);
    +            CompletableFuture future = wrapWithFuture(value);
                 CompletableFuture appResponseFuture = future.handle((obj, t) -> {
    -                AppResponse result = new AppResponse();
    +                AppResponse result = new AppResponse(invocation);
                     if (t != null) {
                         if (t instanceof CompletionException) {
                             result.setException(t.getCause());
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
    index e3df54faad..6117b960d4 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
    @@ -26,6 +26,7 @@ import org.apache.dubbo.rpc.RpcServiceContext;
     import org.apache.dubbo.rpc.model.ApplicationModel;
     import org.apache.dubbo.rpc.model.ConsumerModel;
     
    +import java.lang.reflect.Field;
     import java.lang.reflect.InvocationHandler;
     import java.lang.reflect.Method;
     
    @@ -39,6 +40,17 @@ public class InvokerInvocationHandler implements InvocationHandler {
         private URL url;
         private String protocolServiceKey;
     
    +    public static Field stackTraceField;
    +
    +    static {
    +        try {
    +            stackTraceField = Throwable.class.getDeclaredField("stackTrace");
    +            stackTraceField.setAccessible(true);
    +        } catch (NoSuchFieldException e) {
    +            // ignore
    +        }
    +    }
    +
         public InvokerInvocationHandler(Invoker handler) {
             this.invoker = handler;
             this.url = invoker.getUrl();
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java
    index 4dd0051022..0b2e263d4c 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java
    @@ -17,6 +17,8 @@
     package org.apache.dubbo.rpc.support;
     
     import org.apache.dubbo.common.utils.StringUtils;
    +import org.apache.dubbo.rpc.Invocation;
    +import org.apache.dubbo.rpc.Invoker;
     import org.apache.dubbo.rpc.RpcContext;
     
     import com.alibaba.fastjson.JSON;
    @@ -28,6 +30,9 @@ import java.util.Date;
     import java.util.HashMap;
     import java.util.Map;
     
    +import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
    +import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
    +
     /**
      * AccessLogData is a container for log event data. In internally uses map and store each filed of log as value. It
      * does not generate any dynamic value e.g. time stamp, local jmv machine host address etc. It does not allow any null
    @@ -262,5 +267,15 @@ public final class AccessLogData {
         private void set(String key, Object value) {
             data.put(key, value);
         }
    +    
    +    public void buildAccessLogData(Invoker invoker, Invocation inv) {
    +        setServiceName(invoker.getInterface().getName());
    +        setMethodName(inv.getMethodName());
    +        setVersion(invoker.getUrl().getParameter(VERSION_KEY));
    +        setGroup(invoker.getUrl().getParameter(GROUP_KEY));
    +        setInvocationTime(new Date());
    +        setTypes(inv.getParameterTypes());
    +        setArguments(inv.getArguments());
    +    }
     
     }
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java
    index 5df3e636f6..823254296b 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java
    @@ -62,7 +62,7 @@ public class RpcUtils {
                     String service = invocation.getInvoker().getUrl().getServiceInterface();
                     if (StringUtils.isNotEmpty(service)) {
                         Method method = getMethodByService(invocation, service);
    -                    return method.getReturnType();
    +                    return method == null ? null : method.getReturnType();
                     }
                 }
             } catch (Throwable t) {
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java
    index 66cf0b571d..4f1e6e9448 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java
    @@ -139,6 +139,7 @@ public class RpcContextTest {
     
             map.keySet().forEach(context::remove);
             Assertions.assertNull(context.get("_11"));
    +        RpcContext.removeContext();
         }
     
         @Test
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/TimeoutCountDownTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/TimeoutCountDownTest.java
    new file mode 100644
    index 0000000000..c83ab017a3
    --- /dev/null
    +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/TimeoutCountDownTest.java
    @@ -0,0 +1,39 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the "License"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *     http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +
    +package org.apache.dubbo.rpc;
    +
    +import org.junit.jupiter.api.Assertions;
    +import org.junit.jupiter.api.Test;
    +
    +import java.util.concurrent.TimeUnit;
    +
    +public class TimeoutCountDownTest {
    +
    +    @Test
    +    public void testTimeoutCountDown() throws InterruptedException {
    +        TimeoutCountDown timeoutCountDown = TimeoutCountDown.newCountDown(5, TimeUnit.SECONDS);
    +        Assertions.assertEquals(5 * 1000, timeoutCountDown.getTimeoutInMilli());
    +        Assertions.assertFalse(timeoutCountDown.isExpired());
    +        Assertions.assertTrue(timeoutCountDown.timeRemaining(TimeUnit.SECONDS) > 0);
    +
    +        Thread.sleep(6 * 1000);
    +
    +        Assertions.assertTrue(timeoutCountDown.isExpired());
    +        Assertions.assertTrue(timeoutCountDown.timeRemaining(TimeUnit.SECONDS) <= 0);
    +    }
    +}
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/AccessLogFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/AccessLogFilterTest.java
    index 353f9a3c41..e883950a9b 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/AccessLogFilterTest.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/AccessLogFilterTest.java
    @@ -17,6 +17,7 @@
     package org.apache.dubbo.rpc.filter;
     
     import org.apache.dubbo.common.URL;
    +import org.apache.dubbo.common.utils.DubboAppender;
     import org.apache.dubbo.common.utils.LogUtil;
     import org.apache.dubbo.rpc.Filter;
     import org.apache.dubbo.rpc.Invocation;
    @@ -51,6 +52,7 @@ public class AccessLogFilterTest {
             accessLogFilter.invoke(invoker, invocation);
             assertEquals(1, LogUtil.findMessage("Exception in AccessLogFilter of service"));
             LogUtil.stop();
    +        DubboAppender.clear();
         }
     
         // TODO how to assert thread action
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/DefaultTPSLimiterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/DefaultTPSLimiterTest.java
    index 6633f587a9..38beb0c65a 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/DefaultTPSLimiterTest.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/DefaultTPSLimiterTest.java
    @@ -24,11 +24,12 @@ import org.junit.jupiter.api.Assertions;
     import org.junit.jupiter.api.Test;
     
     import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
    -import static org.apache.dubbo.rpc.Constants.TPS_LIMIT_RATE_KEY;
     import static org.apache.dubbo.rpc.Constants.TPS_LIMIT_INTERVAL_KEY;
    +import static org.apache.dubbo.rpc.Constants.TPS_LIMIT_RATE_KEY;
     
     public class DefaultTPSLimiterTest {
     
    +    private static final int TEST_LIMIT_RATE = 2;
         private DefaultTPSLimiter defaultTPSLimiter = new DefaultTPSLimiter();
     
         @Test
    @@ -36,9 +37,9 @@ public class DefaultTPSLimiterTest {
             Invocation invocation = new MockInvocation();
             URL url = URL.valueOf("test://test");
             url = url.addParameter(INTERFACE_KEY, "org.apache.dubbo.rpc.file.TpsService");
    -        url = url.addParameter(TPS_LIMIT_RATE_KEY, 2);
    +        url = url.addParameter(TPS_LIMIT_RATE_KEY, TEST_LIMIT_RATE);
             url = url.addParameter(TPS_LIMIT_INTERVAL_KEY, 1000);
    -        for (int i = 0; i < 3; i++) {
    +        for (int i = 1; i <= TEST_LIMIT_RATE; i++) {
                 Assertions.assertTrue(defaultTPSLimiter.isAllowable(url, invocation));
             }
         }
    @@ -48,10 +49,10 @@ public class DefaultTPSLimiterTest {
             Invocation invocation = new MockInvocation();
             URL url = URL.valueOf("test://test");
             url = url.addParameter(INTERFACE_KEY, "org.apache.dubbo.rpc.file.TpsService");
    -        url = url.addParameter(TPS_LIMIT_RATE_KEY, 2);
    +        url = url.addParameter(TPS_LIMIT_RATE_KEY, TEST_LIMIT_RATE);
             url = url.addParameter(TPS_LIMIT_INTERVAL_KEY, 1000);
    -        for (int i = 0; i < 4; i++) {
    -            if (i == 3) {
    +        for (int i = 1; i <= TEST_LIMIT_RATE + 1; i++) {
    +            if (i == TEST_LIMIT_RATE + 1) {
                     Assertions.assertFalse(defaultTPSLimiter.isAllowable(url, invocation));
                 } else {
                     Assertions.assertTrue(defaultTPSLimiter.isAllowable(url, invocation));
    @@ -65,14 +66,17 @@ public class DefaultTPSLimiterTest {
             Invocation invocation = new MockInvocation();
             URL url = URL.valueOf("test://test");
             url = url.addParameter(INTERFACE_KEY, "org.apache.dubbo.rpc.file.TpsService");
    -        url = url.addParameter(TPS_LIMIT_RATE_KEY, 2);
    +        url = url.addParameter(TPS_LIMIT_RATE_KEY, TEST_LIMIT_RATE);
             url = url.addParameter(TPS_LIMIT_INTERVAL_KEY, 1000);
    -        for (int i = 0; i < 3; i++) {
    +        for (int i = 1; i <= TEST_LIMIT_RATE; i++) {
                 Assertions.assertTrue(defaultTPSLimiter.isAllowable(url, invocation));
             }
    -        url = url.addParameter(TPS_LIMIT_RATE_KEY, 2000);
    -        for (int i = 0; i < 3; i++) {
    +        final int tenTimesLimitRate = TEST_LIMIT_RATE * 10;
    +        url = url.addParameter(TPS_LIMIT_RATE_KEY, tenTimesLimitRate);
    +        for (int i = 1; i <= tenTimesLimitRate; i++) {
                 Assertions.assertTrue(defaultTPSLimiter.isAllowable(url, invocation));
             }
    +        
    +        Assertions.assertFalse(defaultTPSLimiter.isAllowable(url, invocation));
         }
     }
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/StatItemTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/StatItemTest.java
    index 78c0c9cb0e..e8c6bf8023 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/StatItemTest.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/StatItemTest.java
    @@ -42,4 +42,15 @@ public class StatItemTest {
             assertEquals(4, statItem.getToken());
         }
     
    +	@Test
    +	public void testAccuracy() throws Exception {
    +		final int EXPECTED_RATE = 5;
    +		statItem = new StatItem("test", EXPECTED_RATE, 60_000L);
    +		for (int i = 1; i <= EXPECTED_RATE; i++) {
    +			assertEquals(true, statItem.isAllowable());
    +		}
    +
    +		// Must block the 6th item
    +		assertEquals(false, statItem.isAllowable());
    +	}
     }
    diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java
    index 00deb91a69..df5bdf382b 100644
    --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java
    +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java
    @@ -18,8 +18,11 @@ package org.apache.dubbo.rpc.support;
     
     import org.apache.dubbo.common.URL;
     import org.apache.dubbo.rpc.Invocation;
    +import org.apache.dubbo.rpc.InvokeMode;
     import org.apache.dubbo.rpc.Invoker;
     import org.apache.dubbo.rpc.RpcInvocation;
    +import org.apache.dubbo.rpc.model.ApplicationModel;
    +import org.apache.dubbo.rpc.model.ServiceRepository;
     
     import org.junit.jupiter.api.Assertions;
     import org.junit.jupiter.api.Test;
    @@ -51,7 +54,7 @@ public class RpcUtilsTest {
             URL url = URL.valueOf("dubbo://localhost/?test.async=true");
             Map attachments = new HashMap<>();
             attachments.put("aa", "bb");
    -        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{}, attachments);
    +        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[] {}, new String[] {}, attachments);
             RpcUtils.attachInvocationIdIfAsync(url, inv);
             long id1 = RpcUtils.getInvocationId(inv);
             RpcUtils.attachInvocationIdIfAsync(url, inv);
    @@ -68,7 +71,7 @@ public class RpcUtilsTest {
         @Test
         public void testAttachInvocationIdIfAsync_sync() {
             URL url = URL.valueOf("dubbo://localhost/");
    -        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
    +        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[] {}, new String[] {});
             RpcUtils.attachInvocationIdIfAsync(url, inv);
             assertNull(RpcUtils.getInvocationId(inv));
         }
    @@ -80,7 +83,7 @@ public class RpcUtilsTest {
         @Test
         public void testAttachInvocationIdIfAsync_nullAttachments() {
             URL url = URL.valueOf("dubbo://localhost/?test.async=true");
    -        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
    +        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[] {}, new String[] {});
             RpcUtils.attachInvocationIdIfAsync(url, inv);
             assertTrue(RpcUtils.getInvocationId(inv) >= 0L);
         }
    @@ -92,7 +95,7 @@ public class RpcUtilsTest {
         @Test
         public void testAttachInvocationIdIfAsync_forceNotAttache() {
             URL url = URL.valueOf("dubbo://localhost/?test.async=true&" + AUTO_ATTACH_INVOCATIONID_KEY + "=false");
    -        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
    +        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[] {}, new String[] {});
             RpcUtils.attachInvocationIdIfAsync(url, inv);
             assertNull(RpcUtils.getInvocationId(inv));
         }
    @@ -104,7 +107,7 @@ public class RpcUtilsTest {
         @Test
         public void testAttachInvocationIdIfAsync_forceAttache() {
             URL url = URL.valueOf("dubbo://localhost/?" + AUTO_ATTACH_INVOCATIONID_KEY + "=true");
    -        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
    +        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[] {}, new String[] {});
             RpcUtils.attachInvocationIdIfAsync(url, inv);
             assertNotNull(RpcUtils.getInvocationId(inv));
         }
    @@ -114,33 +117,35 @@ public class RpcUtilsTest {
             Class demoServiceClass = DemoService.class;
             String serviceName = demoServiceClass.getName();
             Invoker invoker = mock(Invoker.class);
    -        given(invoker.getUrl()).willReturn(URL.valueOf("test://127.0.0.1:1/org.apache.dubbo.rpc.support.DemoService?interface=org.apache.dubbo.rpc.support.DemoService"));
    +        given(invoker.getUrl()).willReturn(URL.valueOf(
    +                "test://127.0.0.1:1/org.apache.dubbo.rpc.support.DemoService?interface=org.apache.dubbo.rpc.support.DemoService"));
     
             // void sayHello(String name);
    -        RpcInvocation inv = new RpcInvocation("sayHello", serviceName, "", new Class[]{String.class}, null, null, invoker, null);
    +        RpcInvocation inv = new RpcInvocation("sayHello", serviceName, "", new Class[] {String.class}, null, null, invoker, null);
             Class returnType = RpcUtils.getReturnType(inv);
             Assertions.assertNull(returnType);
     
             //String echo(String text);
    -        RpcInvocation inv1 = new RpcInvocation("echo", serviceName, "", new Class[]{String.class}, null, null, invoker, null);
    +        RpcInvocation inv1 = new RpcInvocation("echo", serviceName, "", new Class[] {String.class}, null, null, invoker, null);
             Class returnType1 = RpcUtils.getReturnType(inv1);
             Assertions.assertNotNull(returnType1);
             Assertions.assertEquals(String.class, returnType1);
     
             //int getSize(String[] strs);
    -        RpcInvocation inv2 = new RpcInvocation("getSize", serviceName, "", new Class[]{String[].class}, null, null, invoker, null);
    +        RpcInvocation inv2 = new RpcInvocation("getSize", serviceName, "", new Class[] {String[].class}, null, null, invoker, null);
             Class returnType2 = RpcUtils.getReturnType(inv2);
             Assertions.assertNotNull(returnType2);
             Assertions.assertEquals(int.class, returnType2);
     
             //Person getPerson(Person person);
    -        RpcInvocation inv3 = new RpcInvocation("getPerson", serviceName, "", new Class[]{Person.class}, null, null, invoker, null);
    +        RpcInvocation inv3 = new RpcInvocation("getPerson", serviceName, "", new Class[] {Person.class}, null, null, invoker, null);
             Class returnType3 = RpcUtils.getReturnType(inv3);
             Assertions.assertNotNull(returnType3);
             Assertions.assertEquals(Person.class, returnType3);
     
             //List testReturnType1(String str);
    -        RpcInvocation inv4 = new RpcInvocation("testReturnType1", serviceName, "", new Class[]{String.class}, null, null, invoker, null);
    +        RpcInvocation inv4 =
    +                new RpcInvocation("testReturnType1", serviceName, "", new Class[] {String.class}, null, null, invoker, null);
             Class returnType4 = RpcUtils.getReturnType(inv4);
             Assertions.assertNotNull(returnType4);
             Assertions.assertEquals(List.class, returnType4);
    @@ -152,9 +157,10 @@ public class RpcUtilsTest {
             Class demoServiceClass = DemoService.class;
             String serviceName = demoServiceClass.getName();
             Invoker invoker = mock(Invoker.class);
    -        given(invoker.getUrl()).willReturn(URL.valueOf("test://127.0.0.1:1/org.apache.dubbo.rpc.support.DemoService?interface=org.apache.dubbo.rpc.support.DemoService"));
    +        given(invoker.getUrl()).willReturn(URL.valueOf(
    +                "test://127.0.0.1:1/org.apache.dubbo.rpc.support.DemoService?interface=org.apache.dubbo.rpc.support.DemoService"));
     
    -        RpcInvocation inv = new RpcInvocation("testReturnType", serviceName, "", new Class[]{String.class}, null, null, invoker, null);
    +        RpcInvocation inv = new RpcInvocation("testReturnType", serviceName, "", new Class[] {String.class}, null, null, invoker, null);
             Type[] types = RpcUtils.getReturnTypes(inv);
             Assertions.assertNotNull(types);
             Assertions.assertEquals(2, types.length);
    @@ -162,7 +168,8 @@ public class RpcUtilsTest {
             Assertions.assertEquals(String.class, types[1]);
             Assertions.assertArrayEquals(types, inv.getReturnTypes());
     
    -        RpcInvocation inv1 = new RpcInvocation("testReturnType1", serviceName, "", new Class[]{String.class}, null, null, invoker, null);
    +        RpcInvocation inv1 =
    +                new RpcInvocation("testReturnType1", serviceName, "", new Class[] {String.class}, null, null, invoker, null);
             java.lang.reflect.Type[] types1 = RpcUtils.getReturnTypes(inv1);
             Assertions.assertNotNull(types1);
             Assertions.assertEquals(2, types1.length);
    @@ -170,7 +177,8 @@ public class RpcUtilsTest {
             Assertions.assertEquals(demoServiceClass.getMethod("testReturnType1", String.class).getGenericReturnType(), types1[1]);
             Assertions.assertArrayEquals(types1, inv1.getReturnTypes());
     
    -        RpcInvocation inv2 = new RpcInvocation("testReturnType2", serviceName, "", new Class[]{String.class}, null, null, invoker, null);
    +        RpcInvocation inv2 =
    +                new RpcInvocation("testReturnType2", serviceName, "", new Class[] {String.class}, null, null, invoker, null);
             java.lang.reflect.Type[] types2 = RpcUtils.getReturnTypes(inv2);
             Assertions.assertNotNull(types2);
             Assertions.assertEquals(2, types2.length);
    @@ -178,7 +186,8 @@ public class RpcUtilsTest {
             Assertions.assertEquals(String.class, types2[1]);
             Assertions.assertArrayEquals(types2, inv2.getReturnTypes());
     
    -        RpcInvocation inv3 = new RpcInvocation("testReturnType3", serviceName, "", new Class[]{String.class}, null, null, invoker, null);
    +        RpcInvocation inv3 =
    +                new RpcInvocation("testReturnType3", serviceName, "", new Class[] {String.class}, null, null, invoker, null);
             java.lang.reflect.Type[] types3 = RpcUtils.getReturnTypes(inv3);
             Assertions.assertNotNull(types3);
             Assertions.assertEquals(2, types3.length);
    @@ -187,7 +196,8 @@ public class RpcUtilsTest {
             Assertions.assertEquals(((ParameterizedType) genericReturnType3).getActualTypeArguments()[0], types3[1]);
             Assertions.assertArrayEquals(types3, inv3.getReturnTypes());
     
    -        RpcInvocation inv4 = new RpcInvocation("testReturnType4", serviceName, "", new Class[]{String.class}, null, null, invoker, null);
    +        RpcInvocation inv4 =
    +                new RpcInvocation("testReturnType4", serviceName, "", new Class[] {String.class}, null, null, invoker, null);
             java.lang.reflect.Type[] types4 = RpcUtils.getReturnTypes(inv4);
             Assertions.assertNotNull(types4);
             Assertions.assertEquals(2, types4.length);
    @@ -195,7 +205,8 @@ public class RpcUtilsTest {
             Assertions.assertNull(types4[1]);
             Assertions.assertArrayEquals(types4, inv4.getReturnTypes());
     
    -        RpcInvocation inv5 = new RpcInvocation("testReturnType5", serviceName, "", new Class[]{String.class}, null, null, invoker, null);
    +        RpcInvocation inv5 =
    +                new RpcInvocation("testReturnType5", serviceName, "", new Class[] {String.class}, null, null, invoker, null);
             java.lang.reflect.Type[] types5 = RpcUtils.getReturnTypes(inv5);
             Assertions.assertNotNull(types5);
             Assertions.assertEquals(2, types5.length);
    @@ -213,7 +224,7 @@ public class RpcUtilsTest {
     
             // void sayHello(String name);
             RpcInvocation inv1 = new RpcInvocation("sayHello", serviceName, "",
    -                new Class[]{String.class}, null, null, invoker, null);
    +                new Class[] {String.class}, null, null, invoker, null);
             Class[] parameterTypes1 = RpcUtils.getParameterTypes(inv1);
             Assertions.assertNotNull(parameterTypes1);
             Assertions.assertEquals(1, parameterTypes1.length);
    @@ -226,7 +237,7 @@ public class RpcUtilsTest {
     
             //Type enumlength(Type... types);
             RpcInvocation inv3 = new RpcInvocation("enumlength", serviceName, "",
    -                new Class[]{Type.class, Type.class}, null, null, invoker, null);
    +                new Class[] {Type.class, Type.class}, null, null, invoker, null);
             Class[] parameterTypes3 = RpcUtils.getParameterTypes(inv3);
             Assertions.assertNotNull(parameterTypes3);
             Assertions.assertEquals(2, parameterTypes3.length);
    @@ -235,7 +246,7 @@ public class RpcUtilsTest {
     
             //byte getbyte(byte arg);
             RpcInvocation inv4 = new RpcInvocation("getbyte", serviceName, "",
    -                new Class[]{byte.class}, null, null, invoker, null);
    +                new Class[] {byte.class}, null, null, invoker, null);
             Class[] parameterTypes4 = RpcUtils.getParameterTypes(inv4);
             Assertions.assertNotNull(parameterTypes4);
             Assertions.assertEquals(1, parameterTypes4.length);
    @@ -243,8 +254,8 @@ public class RpcUtilsTest {
     
             //void $invoke(String s1, String s2);
             RpcInvocation inv5 = new RpcInvocation("$invoke", serviceName, "",
    -                new Class[]{String.class, String[].class},
    -                new Object[]{"method", new String[]{"java.lang.String", "void", "java.lang.Object"}},
    +                new Class[] {String.class, String[].class},
    +                new Object[] {"method", new String[] {"java.lang.String", "void", "java.lang.Object"}},
                     null, invoker, null);
             Class[] parameterTypes5 = RpcUtils.getParameterTypes(inv5);
             Assertions.assertNotNull(parameterTypes5);
    @@ -266,7 +277,7 @@ public class RpcUtilsTest {
             Invoker invoker = mock(Invoker.class);
     
             RpcInvocation inv1 = new RpcInvocation(methodName, serviceName, "",
    -                new Class[]{String.class}, null, null, invoker, null);
    +                new Class[] {String.class}, null, null, invoker, null);
             String actual = RpcUtils.getMethodName(inv1);
             Assertions.assertNotNull(actual);
             Assertions.assertEquals(methodName, actual);
    @@ -284,8 +295,8 @@ public class RpcUtilsTest {
             Invoker invoker = mock(Invoker.class);
     
             RpcInvocation inv = new RpcInvocation("$invoke", serviceName, "",
    -                new Class[]{String.class, String[].class},
    -                new Object[]{method, new String[]{"java.lang.String", "void", "java.lang.Object"}},
    +                new Class[] {String.class, String[].class},
    +                new Object[] {method, new String[] {"java.lang.String", "void", "java.lang.Object"}},
                     null, invoker, null);
             String actual = RpcUtils.getMethodName(inv);
             Assertions.assertNotNull(actual);
    @@ -295,14 +306,14 @@ public class RpcUtilsTest {
     
         @Test
         public void testGet_$invoke_Arguments() {
    -        Object[] args = new Object[]{"hello", "dubbo", 520};
    +        Object[] args = new Object[] {"hello", "dubbo", 520};
             Class demoServiceClass = DemoService.class;
             String serviceName = demoServiceClass.getName();
             Invoker invoker = mock(Invoker.class);
     
             RpcInvocation inv = new RpcInvocation("$invoke", serviceName, "",
    -                new Class[]{String.class, String[].class, Object[].class},
    -                new Object[]{"method", new String[]{}, args},
    +                new Class[] {String.class, String[].class, Object[].class},
    +                new Object[] {"method", new String[] {}, args},
                     null, invoker, null);
     
             Object[] arguments = RpcUtils.getArguments(inv);
    @@ -313,4 +324,55 @@ public class RpcUtilsTest {
             }
         }
     
    +    @Test
    +    public void testIsAsync() {
    +        Object[] args = new Object[] {"hello", "dubbo", 520};
    +        Class demoServiceClass = DemoService.class;
    +        String serviceName = demoServiceClass.getName();
    +        Invoker invoker = mock(Invoker.class);
    +
    +        URL url = URL.valueOf(
    +                "test://127.0.0.1:1/org.apache.dubbo.rpc.support.DemoService?interface=org.apache.dubbo.rpc.support.DemoService");
    +
    +        RpcInvocation inv = new RpcInvocation("test", serviceName, "",
    +                new Class[] {String.class, String[].class, Object[].class},
    +                new Object[] {"method", new String[] {}, args},
    +                null, invoker, null);
    +
    +        Assertions.assertFalse(RpcUtils.isAsync(url, inv));
    +        inv.setInvokeMode(InvokeMode.ASYNC);
    +        Assertions.assertTrue(RpcUtils.isAsync(url, inv));
    +    }
    +
    +    @Test
    +    public void testIsGenericCall() {
    +        Assertions.assertTrue(RpcUtils.isGenericCall("Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/Object;", "$invoke"));
    +        Assertions.assertTrue(RpcUtils.isGenericCall("Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/Object;", "$invokeAsync"));
    +        Assertions.assertFalse(RpcUtils.isGenericCall("Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/Object;", "testMethod"));
    +    }
    +
    +    @Test
    +    public void testIsEcho() {
    +        Assertions.assertTrue(RpcUtils.isEcho("Ljava/lang/Object;", "$echo"));
    +        Assertions.assertFalse(RpcUtils.isEcho("Ljava/lang/Object;", "testMethod"));
    +        Assertions.assertFalse(RpcUtils.isEcho("Ljava/lang/String;", "$echo"));
    +    }
    +    @Test
    +    public void testIsReturnTypeFuture() {
    +        Class demoServiceClass = DemoService.class;
    +        String serviceName = demoServiceClass.getName();
    +        Invoker invoker = mock(Invoker.class);
    +        given(invoker.getUrl()).willReturn(URL.valueOf(
    +                "test://127.0.0.1:1/org.apache.dubbo.rpc.support.DemoService?interface=org.apache.dubbo.rpc.support.DemoService"));
    +
    +        RpcInvocation inv = new RpcInvocation("testReturnType", serviceName, "", new Class[] {String.class}, null, null, invoker, null);
    +        Assertions.assertFalse(RpcUtils.isReturnTypeFuture(inv));
    +
    +        ServiceRepository repository = ApplicationModel.getServiceRepository();
    +        repository.registerService(demoServiceClass);
    +
    +        inv = new RpcInvocation("testReturnType4", serviceName, "", new Class[] {String.class}, null, null, invoker, null);
    +        Assertions.assertTrue(RpcUtils.isReturnTypeFuture(inv));
    +    }
    +
     }
    diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java
    index fdd3ed6822..afedeeb932 100644
    --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java
    +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java
    @@ -17,6 +17,7 @@
     package org.apache.dubbo.rpc.protocol.dubbo;
     
     
    +import org.apache.dubbo.common.config.ConfigurationUtils;
     import org.apache.dubbo.common.logger.Logger;
     import org.apache.dubbo.common.logger.LoggerFactory;
     import org.apache.dubbo.common.serialize.Cleanable;
    @@ -47,6 +48,8 @@ import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KE
     import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
     import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY;
     import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
    +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_ID_KEY;
    +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_SECURITY_CHECK_KEY;
     import static org.apache.dubbo.rpc.protocol.dubbo.CallbackServiceCodec.decodeInvocationArgument;
     
     public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Decodeable {
    @@ -95,10 +98,15 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec
             throw new UnsupportedOperationException();
         }
     
    +    private void checkSerializationTypeFromRemote() {
    +
    +    }
    +
         @Override
         public Object decode(Channel channel, InputStream input) throws IOException {
             ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType)
                     .deserialize(channel.getUrl(), input);
    +        this.put(SERIALIZATION_ID_KEY, serializationType);
     
             String dubboVersion = in.readUTF();
             request.setVersion(dubboVersion);
    @@ -106,7 +114,8 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec
     
             String path = in.readUTF();
             setAttachment(PATH_KEY, path);
    -        setAttachment(VERSION_KEY, in.readUTF());
    +        String version = in.readUTF();
    +        setAttachment(VERSION_KEY, version);
     
             setMethodName(in.readUTF());
     
    @@ -114,6 +123,9 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec
             setParameterTypesDesc(desc);
     
             try {
    +            if (ConfigurationUtils.getSystemConfiguration().getBoolean(SERIALIZATION_SECURITY_CHECK_KEY, false)) {
    +                CodecSupport.checkSerialization(path, version, serializationType);
    +            }
                 Object[] args = DubboCodec.EMPTY_OBJECT_ARRAY;
                 Class[] pts = DubboCodec.EMPTY_CLASS_ARRAY;
                 if (desc.length() > 0) {
    diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java
    index 83db98600b..30ee3a0e3e 100644
    --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java
    +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java
    @@ -16,6 +16,7 @@
      */
     package org.apache.dubbo.rpc.protocol.dubbo;
     
    +import org.apache.dubbo.common.config.ConfigurationUtils;
     import org.apache.dubbo.common.logger.Logger;
     import org.apache.dubbo.common.logger.LoggerFactory;
     import org.apache.dubbo.common.serialize.Cleanable;
    @@ -38,6 +39,9 @@ import java.io.InputStream;
     import java.io.OutputStream;
     import java.lang.reflect.Type;
     
    +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_ID_KEY;
    +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_SECURITY_CHECK_KEY;
    +
     public class DecodeableRpcResult extends AppResponse implements Codec, Decodeable {
     
         private static final Logger log = LoggerFactory.getLogger(DecodeableRpcResult.class);
    @@ -114,6 +118,14 @@ public class DecodeableRpcResult extends AppResponse implements Codec, Decodeabl
         public void decode() throws Exception {
             if (!hasDecoded && channel != null && inputStream != null) {
                 try {
    +                if (ConfigurationUtils.getSystemConfiguration().getBoolean(SERIALIZATION_SECURITY_CHECK_KEY, false)) {
    +                    Object serializationType_obj = invocation.get(SERIALIZATION_ID_KEY);
    +                    if (serializationType_obj != null) {
    +                        if ((byte) serializationType_obj != serializationType) {
    +                            throw new IOException("Unexpected serialization id:" + serializationType + " received from network, please check if the peer send the right id.");
    +                        }
    +                    }
    +                }
                     decode(channel, inputStream);
                 } catch (Throwable e) {
                     if (log.isWarnEnabled()) {
    @@ -160,7 +172,7 @@ public class DecodeableRpcResult extends AppResponse implements Codec, Decodeabl
     
         private void handleAttachment(ObjectInput in) throws IOException {
             try {
    -            setObjectAttachments(in.readAttachments());
    +            addObjectAttachments(in.readAttachments());
             } catch (ClassNotFoundException e) {
                 rethrow(e);
             }
    diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java
    index 889f2cebad..c6c7994de3 100644
    --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java
    +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java
    @@ -23,16 +23,19 @@ import org.apache.dubbo.common.logger.Logger;
     import org.apache.dubbo.common.logger.LoggerFactory;
     import org.apache.dubbo.common.serialize.ObjectInput;
     import org.apache.dubbo.common.serialize.ObjectOutput;
    +import org.apache.dubbo.common.serialize.Serialization;
     import org.apache.dubbo.common.utils.StringUtils;
     import org.apache.dubbo.remoting.Channel;
     import org.apache.dubbo.remoting.exchange.Request;
     import org.apache.dubbo.remoting.exchange.Response;
     import org.apache.dubbo.remoting.exchange.codec.ExchangeCodec;
     import org.apache.dubbo.remoting.transport.CodecSupport;
    +import org.apache.dubbo.rpc.AppResponse;
     import org.apache.dubbo.rpc.Invocation;
     import org.apache.dubbo.rpc.Result;
     import org.apache.dubbo.rpc.RpcInvocation;
     
    +import java.io.ByteArrayInputStream;
     import java.io.IOException;
     import java.io.InputStream;
     
    @@ -79,8 +82,14 @@ public class DubboCodec extends ExchangeCodec {
                     if (status == Response.OK) {
                         Object data;
                         if (res.isEvent()) {
    -                        ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto);
    -                        data = decodeEventData(channel, in);
    +                        byte[] eventPayload = CodecSupport.getPayload(is);
    +                        if (CodecSupport.isHeartBeat(eventPayload, proto)) {
    +                            // heart beat response data is always null;
    +                            data = null;
    +                        } else {
    +                            ObjectInput in = CodecSupport.deserialize(channel.getUrl(), new ByteArrayInputStream(eventPayload), proto);
    +                            data = decodeEventData(channel, in, eventPayload);
    +                        }
                         } else {
                             DecodeableRpcResult result;
                             if (channel.getUrl().getParameter(DECODE_IN_IO_THREAD_KEY, DEFAULT_DECODE_IN_IO_THREAD)) {
    @@ -118,8 +127,14 @@ public class DubboCodec extends ExchangeCodec {
                 try {
                     Object data;
                     if (req.isEvent()) {
    -                    ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto);
    -                    data = decodeEventData(channel, in);
    +                    byte[] eventPayload = CodecSupport.getPayload(is);
    +                    if (CodecSupport.isHeartBeat(eventPayload, proto)) {
    +                        // heart beat response data is always null;
    +                        data = null;
    +                    } else {
    +                        ObjectInput in = CodecSupport.deserialize(channel.getUrl(), new ByteArrayInputStream(eventPayload), proto);
    +                        data = decodeEventData(channel, in, eventPayload);
    +                    }
                     } else {
                         DecodeableRpcInvocation inv;
                         if (channel.getUrl().getParameter(DECODE_IN_IO_THREAD_KEY, DEFAULT_DECODE_IN_IO_THREAD)) {
    @@ -213,4 +228,21 @@ public class DubboCodec extends ExchangeCodec {
                 out.writeAttachments(result.getObjectAttachments());
             }
         }
    +
    +    @Override
    +    protected Serialization getSerialization(Channel channel, Request req) {
    +        if (!(req.getData() instanceof Invocation)) {
    +            return super.getSerialization(channel, req);
    +        }
    +        return DubboCodecSupport.getRequestSerialization(channel.getUrl(), (Invocation) req.getData());
    +    }
    +
    +    @Override
    +    protected Serialization getSerialization(Channel channel, Response res) {
    +        if (!(res.getResult() instanceof AppResponse)) {
    +            return super.getSerialization(channel, res);
    +        }
    +        return DubboCodecSupport.getResponseSerialization(channel.getUrl(), (AppResponse) res.getResult());
    +    }
    +
     }
    diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodecSupport.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodecSupport.java
    new file mode 100644
    index 0000000000..2dbb312b43
    --- /dev/null
    +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodecSupport.java
    @@ -0,0 +1,53 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the "License"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *     http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.dubbo.rpc.protocol.dubbo;
    +
    +import org.apache.dubbo.common.URL;
    +import org.apache.dubbo.common.extension.ExtensionLoader;
    +import org.apache.dubbo.common.serialize.Serialization;
    +import org.apache.dubbo.remoting.Constants;
    +import org.apache.dubbo.remoting.transport.CodecSupport;
    +import org.apache.dubbo.rpc.AppResponse;
    +import org.apache.dubbo.rpc.Invocation;
    +
    +import static org.apache.dubbo.rpc.Constants.INVOCATION_KEY;
    +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_ID_KEY;
    +
    +public class DubboCodecSupport {
    +
    +    public static Serialization getRequestSerialization(URL url, Invocation invocation) {
    +        Object serializationType_obj = invocation.get(SERIALIZATION_ID_KEY);
    +        if (serializationType_obj != null) {
    +            return CodecSupport.getSerializationById((byte) serializationType_obj);
    +        }
    +        return ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(
    +                url.getParameter(org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));
    +    }
    +
    +    public static Serialization getResponseSerialization(URL url, AppResponse appResponse) {
    +        Object invocation_obj = appResponse.getAttribute(INVOCATION_KEY);
    +        if (invocation_obj != null) {
    +            Invocation invocation = (Invocation) invocation_obj;
    +            Object serializationType_obj = invocation.get(SERIALIZATION_ID_KEY);
    +            if (serializationType_obj != null) {
    +                return CodecSupport.getSerializationById((byte) serializationType_obj);
    +            }
    +        }
    +        return ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(
    +                url.getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));
    +    }
    +}
    diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboExporter.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboExporter.java
    index 2f9c2eb9eb..57c1954f43 100644
    --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboExporter.java
    +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboExporter.java
    @@ -38,8 +38,7 @@ public class DubboExporter extends AbstractExporter {
         }
     
         @Override
    -    public void unexport() {
    -        super.unexport();
    +    public void afterUnExport() {
             exporterMap.remove(key);
         }
     
    diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java
    index 037f233d6c..85244e9a45 100644
    --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java
    +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java
    @@ -43,6 +43,7 @@ import java.util.concurrent.TimeUnit;
     import java.util.concurrent.locks.ReentrantLock;
     
     import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
    +import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_VERSION;
     import static org.apache.dubbo.common.constants.CommonConstants.ENABLE_TIMEOUT_COUNTDOWN_KEY;
     import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
     import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
    @@ -76,7 +77,7 @@ public class DubboInvoker extends AbstractInvoker {
             super(serviceType, url, new String[]{INTERFACE_KEY, GROUP_KEY, TOKEN_KEY});
             this.clients = clients;
             // get version.
    -        this.version = url.getVersion("0.0.0");
    +        this.version = url.getVersion(DEFAULT_VERSION);
             this.invokers = invokers;
         }
     
    diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
    index 434c5c9daa..0a8345e618 100644
    --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
    +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
    @@ -57,7 +57,6 @@ import java.util.Map;
     import java.util.Set;
     import java.util.concurrent.CompletableFuture;
     import java.util.concurrent.ConcurrentHashMap;
    -import java.util.concurrent.ConcurrentMap;
     import java.util.function.Function;
     
     import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
    @@ -100,9 +99,10 @@ public class DubboProtocol extends AbstractProtocol {
     
         /**
          * 
    +     * {@link Map}
          */
    -    private final Map> referenceClientMap = new ConcurrentHashMap<>();
    -    private final ConcurrentMap locks = new ConcurrentHashMap<>();
    +    private final Map referenceClientMap = new ConcurrentHashMap<>();
    +    private static final Object PENDING_OBJECT = new Object();
         private final Set optimizers = new ConcurrentHashSet<>();
     
         private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
    @@ -221,6 +221,7 @@ public class DubboProtocol extends AbstractProtocol {
             return INSTANCE;
         }
     
    +    @Override
         public Collection> getExporters() {
             return Collections.unmodifiableCollection(exporterMap.values());
         }
    @@ -388,11 +389,9 @@ public class DubboProtocol extends AbstractProtocol {
             } catch (ClassNotFoundException e) {
                 throw new RpcException("Cannot find the serialization optimizer class: " + className, e);
     
    -        } catch (InstantiationException e) {
    +        } catch (InstantiationException | IllegalAccessException e) {
                 throw new RpcException("Cannot instantiate the serialization optimizer class: " + className, e);
     
    -        } catch (IllegalAccessException e) {
    -            throw new RpcException("Cannot instantiate the serialization optimizer class: " + className, e);
             }
         }
     
    @@ -451,53 +450,76 @@ public class DubboProtocol extends AbstractProtocol {
          * @param url
          * @param connectNum connectNum must be greater than or equal to 1
          */
    +    @SuppressWarnings("unchecked")
         private List getSharedClient(URL url, int connectNum) {
             String key = url.getAddress();
    -        List clients = referenceClientMap.get(key);
     
    -        if (checkClientCanUse(clients)) {
    -            batchClientRefIncr(clients);
    -            return clients;
    +        Object clients = referenceClientMap.get(key);
    +        if (clients instanceof List) {
    +            List typedClients = (List) clients;
    +            if (checkClientCanUse(typedClients)) {
    +                batchClientRefIncr(typedClients);
    +                return typedClients;
    +            }
             }
     
    -        locks.putIfAbsent(key, new Object());
    -        synchronized (locks.get(key)) {
    -            clients = referenceClientMap.get(key);
    -            // dubbo check
    -            if (checkClientCanUse(clients)) {
    -                batchClientRefIncr(clients);
    -                return clients;
    -            }
    +        List typedClients = null;
     
    +        synchronized (referenceClientMap) {
    +            for (; ; ) {
    +                clients = referenceClientMap.get(key);
    +
    +                if (clients instanceof List) {
    +                    typedClients = (List) clients;
    +                    if (checkClientCanUse(typedClients)) {
    +                        batchClientRefIncr(typedClients);
    +                        return typedClients;
    +                    } else {
    +                        referenceClientMap.put(key, PENDING_OBJECT);
    +                        break;
    +                    }
    +                } else if (clients == PENDING_OBJECT) {
    +                    try {
    +                        referenceClientMap.wait();
    +                    } catch (InterruptedException ignored) {
    +                    }
    +                } else {
    +                    referenceClientMap.put(key, PENDING_OBJECT);
    +                    break;
    +                }
    +            }
    +        }
    +
    +        try {
                 // connectNum must be greater than or equal to 1
                 connectNum = Math.max(connectNum, 1);
     
                 // If the clients is empty, then the first initialization is
    -            if (CollectionUtils.isEmpty(clients)) {
    -                clients = buildReferenceCountExchangeClientList(url, connectNum);
    -                referenceClientMap.put(key, clients);
    -
    +            if (CollectionUtils.isEmpty(typedClients)) {
    +                typedClients = buildReferenceCountExchangeClientList(url, connectNum);
                 } else {
    -                for (int i = 0; i < clients.size(); i++) {
    -                    ReferenceCountExchangeClient referenceCountExchangeClient = clients.get(i);
    +                for (int i = 0; i < typedClients.size(); i++) {
    +                    ReferenceCountExchangeClient referenceCountExchangeClient = typedClients.get(i);
                         // If there is a client in the list that is no longer available, create a new one to replace him.
                         if (referenceCountExchangeClient == null || referenceCountExchangeClient.isClosed()) {
    -                        clients.set(i, buildReferenceCountExchangeClient(url));
    +                        typedClients.set(i, buildReferenceCountExchangeClient(url));
                             continue;
                         }
    -
                         referenceCountExchangeClient.incrementAndGetCount();
                     }
                 }
    -
    -            /*
    -             * I understand that the purpose of the remove operation here is to avoid the expired url key
    -             * always occupying this memory space.
    -             */
    -            locks.remove(key);
    -
    -            return clients;
    +        } finally {
    +            synchronized (referenceClientMap) {
    +                if (typedClients == null) {
    +                    referenceClientMap.remove(key);
    +                } else {
    +                    referenceClientMap.put(key, typedClients);
    +                }
    +                referenceClientMap.notifyAll();
    +            }
             }
    +        return typedClients;
    +
         }
     
         /**
    @@ -605,6 +627,7 @@ public class DubboProtocol extends AbstractProtocol {
         }
     
         @Override
    +    @SuppressWarnings("unchecked")
         public void destroy() {
             for (String key : new ArrayList<>(serverMap.keySet())) {
                 ProtocolServer protocolServer = serverMap.remove(key);
    @@ -628,14 +651,17 @@ public class DubboProtocol extends AbstractProtocol {
             }
     
             for (String key : new ArrayList<>(referenceClientMap.keySet())) {
    -            List clients = referenceClientMap.remove(key);
    +            Object clients = referenceClientMap.remove(key);
    +            if (clients instanceof List) {
    +                List typedClients = (List) clients;
     
    -            if (CollectionUtils.isEmpty(clients)) {
    -                continue;
    -            }
    +                if (CollectionUtils.isEmpty(typedClients)) {
    +                    continue;
    +                }
     
    -            for (ReferenceCountExchangeClient client : clients) {
    -                closeReferenceCountExchangeClient(client);
    +                for (ReferenceCountExchangeClient client : typedClients) {
    +                    closeReferenceCountExchangeClient(client);
    +                }
                 }
             }
     
    diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java
    index 8c4d07be70..a3b9128c9c 100644
    --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java
    +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java
    @@ -19,6 +19,8 @@ package org.apache.dubbo.rpc.protocol.dubbo;
     
     import org.apache.dubbo.common.Parameters;
     import org.apache.dubbo.common.URL;
    +import org.apache.dubbo.common.logger.Logger;
    +import org.apache.dubbo.common.logger.LoggerFactory;
     import org.apache.dubbo.remoting.ChannelHandler;
     import org.apache.dubbo.remoting.RemotingException;
     import org.apache.dubbo.remoting.exchange.ExchangeClient;
    @@ -29,7 +31,6 @@ import java.util.concurrent.CompletableFuture;
     import java.util.concurrent.ExecutorService;
     import java.util.concurrent.atomic.AtomicInteger;
     
    -import static org.apache.dubbo.remoting.Constants.RECONNECT_KEY;
     import static org.apache.dubbo.remoting.Constants.SEND_RECONNECT_KEY;
     import static org.apache.dubbo.rpc.protocol.dubbo.Constants.LAZY_CONNECT_INITIAL_STATE_KEY;
     
    @@ -39,9 +40,11 @@ import static org.apache.dubbo.rpc.protocol.dubbo.Constants.LAZY_CONNECT_INITIAL
     @SuppressWarnings("deprecation")
     final class ReferenceCountExchangeClient implements ExchangeClient {
     
    +    private final static Logger logger = LoggerFactory.getLogger(ReferenceCountExchangeClient.class);
         private final URL url;
         private final AtomicInteger referenceCount = new AtomicInteger(0);
    -
    +    private final AtomicInteger disconnectCount = new AtomicInteger(0);
    +    private final Integer maxDisconnectCount = 50;
         private ExchangeClient client;
     
         public ReferenceCountExchangeClient(ExchangeClient client) {
    @@ -181,9 +184,13 @@ final class ReferenceCountExchangeClient implements ExchangeClient {
         private void replaceWithLazyClient() {
             // this is a defensive operation to avoid client is closed by accident, the initial state of the client is false
             URL lazyUrl = url.addParameter(LAZY_CONNECT_INITIAL_STATE_KEY, Boolean.TRUE)
    -                .addParameter(RECONNECT_KEY, Boolean.FALSE)
    -                .addParameter(SEND_RECONNECT_KEY, Boolean.TRUE.toString())
    -                .addParameter(LazyConnectExchangeClient.REQUEST_WITH_WARNING_KEY, true);
    +                //.addParameter(RECONNECT_KEY, Boolean.FALSE)
    +                .addParameter(SEND_RECONNECT_KEY, Boolean.TRUE.toString());
    +        //.addParameter(LazyConnectExchangeClient.REQUEST_WITH_WARNING_KEY, true);
    +
    +        if (disconnectCount.getAndIncrement() % maxDisconnectCount == 0) {
    +            logger.warn(url.getAddress() + " " + url.getServiceKey() + " safe guard client , should not be called ,must have a bug.");
    +        }
     
             /**
              * the order of judgment in the if statement cannot be changed.
    @@ -204,5 +211,9 @@ final class ReferenceCountExchangeClient implements ExchangeClient {
         public void incrementAndGetCount() {
             referenceCount.incrementAndGet();
         }
    +
    +    public int getCount(){
    +        return referenceCount.get();
    +    }
     }
     
    diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvailableTest.java
    similarity index 99%
    rename from dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
    rename to dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvailableTest.java
    index a3f1af35ce..1ac3063c1b 100644
    --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
    +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvailableTest.java
    @@ -42,7 +42,7 @@ import static org.junit.jupiter.api.Assertions.fail;
     /**
      * Check available status for dubboInvoker
      */
    -public class DubboInvokerAvilableTest {
    +public class DubboInvokerAvailableTest {
         private static DubboProtocol protocol;
         private static ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
     
    @@ -90,6 +90,7 @@ public class DubboInvokerAvilableTest {
         }
     
         @Disabled
    +    @Test
         public void test_normal_channel_close_wait_gracefully() throws Exception {
             int testPort = NetUtils.getAvailablePort();
             URL url = URL.valueOf("dubbo://127.0.0.1:" + testPort + "/org.apache.dubbo.rpc.protocol.dubbo.IDemoService?scope=true&lazy=false");
    diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java
    index 13d43b2c10..e3141c828d 100644
    --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java
    +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java
    @@ -248,4 +248,22 @@ public class DubboProtocolTest {
             Thread.sleep(10);
             assertEquals(result.getValue(), "consumer");
         }
    +
    +    @Test
    +    public void testPayloadOverException() throws Exception {
    +        DemoService service = new DemoServiceImpl();
    +        int port = NetUtils.getAvailablePort();
    +        protocol.export(proxy.getInvoker(service, DemoService.class,
    +                URL.valueOf("dubbo://127.0.0.1:" + port + "/" + DemoService.class.getName()).addParameter("payload", 10 * 1024)));
    +        service = proxy.getProxy(protocol.refer(DemoService.class,
    +                URL.valueOf("dubbo://127.0.0.1:" + port + "/" + DemoService.class.getName()).addParameter("timeout",
    +                        6000L).addParameter("payload", 160)));
    +        try {
    +            service.download(300);
    +            Assertions.fail();
    +        } catch (Exception expected) {
    +            Assertions.assertTrue(expected.getMessage().contains("Data length too large"));
    +        }
    +
    +    }
     }
    diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/DubboTelnetDecodeTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/DubboTelnetDecodeTest.java
    index 2270bcb761..28ad49ed02 100644
    --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/DubboTelnetDecodeTest.java
    +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/DubboTelnetDecodeTest.java
    @@ -43,6 +43,7 @@ import io.netty.channel.embedded.EmbeddedChannel;
     import org.junit.jupiter.api.AfterAll;
     import org.junit.jupiter.api.Assertions;
     import org.junit.jupiter.api.BeforeAll;
    +import org.junit.jupiter.api.Disabled;
     import org.junit.jupiter.api.Test;
     
     import java.io.IOException;
    @@ -262,7 +263,8 @@ public class DubboTelnetDecodeTest {
          *
          * @throws InterruptedException
          */
    -    // @Test
    +    @Disabled
    +    @Test
         public void testTelnetTelnetDecoded() throws InterruptedException {
             ByteBuf firstByteBuf = Unpooled.wrappedBuffer("ls\r".getBytes());
             ByteBuf secondByteBuf = Unpooled.wrappedBuffer("\nls\r\n".getBytes());
    diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoService.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoService.java
    index 40ca4fbce7..fea53d66ac 100644
    --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoService.java
    +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoService.java
    @@ -66,4 +66,6 @@ public interface DemoService {
         String getPerson(Man man);
     
         String getRemoteApplicationName();
    +
    +    byte[] download(int size);
     }
    \ No newline at end of file
    diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java
    index ee0baed40d..89bbb37bcb 100644
    --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java
    +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java
    @@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.protocol.dubbo.support;
     
     import org.apache.dubbo.rpc.RpcContext;
     
    +import java.util.Arrays;
     import java.util.Map;
     import java.util.Set;
     
    @@ -126,4 +127,11 @@ public class DemoServiceImpl implements DemoService {
         public String getRemoteApplicationName() {
             return RpcContext.getServiceContext().getRemoteApplicationName();
         }
    +
    +    @Override
    +    public byte[] download(int size) {
    +        byte[] bytes = new byte[size];
    +        Arrays.fill(bytes, (byte) 0);
    +        return bytes;
    +    }
     }
    \ No newline at end of file
    diff --git a/dubbo-rpc/dubbo-rpc-grpc/src/main/java/org/apache/dubbo/rpc/protocol/grpc/DubboHandlerRegistry.java b/dubbo-rpc/dubbo-rpc-grpc/src/main/java/org/apache/dubbo/rpc/protocol/grpc/DubboHandlerRegistry.java
    index aa9a17ba08..6252e95aa9 100644
    --- a/dubbo-rpc/dubbo-rpc-grpc/src/main/java/org/apache/dubbo/rpc/protocol/grpc/DubboHandlerRegistry.java
    +++ b/dubbo-rpc/dubbo-rpc-grpc/src/main/java/org/apache/dubbo/rpc/protocol/grpc/DubboHandlerRegistry.java
    @@ -36,7 +36,8 @@ public class DubboHandlerRegistry extends HandlerRegistry {
         private final Map services = new ConcurrentHashMap<>();
         private final Map> methods = new ConcurrentHashMap<>();
     
    -    public DubboHandlerRegistry() {}
    +    public DubboHandlerRegistry() {
    +    }
     
         /**
          * Returns the service definitions in this registry.
    @@ -63,8 +64,10 @@ public class DubboHandlerRegistry extends HandlerRegistry {
     
         void removeService(String serviceKey) {
             ServerServiceDefinition service = services.remove(serviceKey);
    -        for (ServerMethodDefinition method : service.getMethods()) {
    -            methods.remove(method.getMethodDescriptor().getFullMethodName(), method);
    +        if (null != service) {
    +            for (ServerMethodDefinition method : service.getMethods()) {
    +                methods.remove(method.getMethodDescriptor().getFullMethodName(), method);
    +            }
             }
         }
     }
    diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
    index 3590e99752..cd899b4971 100644
    --- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
    +++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
    @@ -39,8 +39,7 @@ class InjvmExporter extends AbstractExporter {
         }
     
         @Override
    -    public void unexport() {
    -        super.unexport();
    +    public void afterUnExport() {
             exporterMap.remove(key);
         }
     
    diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
    index 4f4237ed45..66594dd7de 100644
    --- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
    +++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
    @@ -17,17 +17,27 @@
     package org.apache.dubbo.rpc.protocol.injvm;
     
     import org.apache.dubbo.common.URL;
    +import org.apache.dubbo.common.extension.ExtensionLoader;
    +import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
    +import org.apache.dubbo.rpc.AppResponse;
    +import org.apache.dubbo.rpc.AsyncRpcResult;
     import org.apache.dubbo.rpc.Constants;
     import org.apache.dubbo.rpc.Exporter;
    +import org.apache.dubbo.rpc.FutureContext;
     import org.apache.dubbo.rpc.Invocation;
    +import org.apache.dubbo.rpc.InvokeMode;
     import org.apache.dubbo.rpc.Result;
     import org.apache.dubbo.rpc.RpcContext;
     import org.apache.dubbo.rpc.RpcException;
    +import org.apache.dubbo.rpc.RpcInvocation;
     import org.apache.dubbo.rpc.protocol.AbstractInvoker;
     
     import java.util.Map;
    +import java.util.concurrent.CompletableFuture;
    +import java.util.concurrent.ExecutorService;
     
     import static org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_VALUE;
    +import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
     
     /**
      * InjvmInvoker
    @@ -38,6 +48,8 @@ class InjvmInvoker extends AbstractInvoker {
     
         private final Map> exporterMap;
     
    +    private final ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
    +
         InjvmInvoker(Class type, URL url, String key, Map> exporterMap) {
             super(type, url);
             this.key = key;
    @@ -67,6 +79,33 @@ class InjvmInvoker extends AbstractInvoker {
             if (serverHasToken) {
                 invocation.setAttachment(Constants.TOKEN_KEY, serverURL.getParameter(Constants.TOKEN_KEY));
             }
    -        return exporter.getInvoker().invoke(invocation);
    +        if (isAsync(exporter.getInvoker().getUrl(), getUrl())) {
    +            ((RpcInvocation) invocation).setInvokeMode(InvokeMode.ASYNC);
    +            // use consumer executor
    +            ExecutorService executor = executorRepository.createExecutorIfAbsent(getUrl());
    +            CompletableFuture appResponseFuture = CompletableFuture.supplyAsync(() -> {
    +                Result result = exporter.getInvoker().invoke(invocation);
    +                if (result.hasException()) {
    +                    return new AppResponse(result.getException());
    +                } else {
    +                    return new AppResponse(result.getValue());
    +                }
    +            }, executor);
    +            // save for 2.6.x compatibility, for example, TraceFilter in Zipkin uses com.alibaba.xxx.FutureAdapter
    +            FutureContext.getContext().setCompatibleFuture(appResponseFuture);
    +            AsyncRpcResult result = new AsyncRpcResult(appResponseFuture, invocation);
    +            result.setExecutor(executor);
    +            return result;
    +        } else {
    +            return exporter.getInvoker().invoke(invocation);
    +        }
         }
    +
    +    private boolean isAsync(URL remoteUrl, URL localUrl) {
    +        if (localUrl.hasParameter(ASYNC_KEY)) {
    +            return localUrl.getParameter(ASYNC_KEY, false);
    +        }
    +        return remoteUrl.getParameter(ASYNC_KEY, false);
    +    }
    +
     }
    diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java
    index 3e681b2711..a61220dbcf 100644
    --- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java
    +++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java
    @@ -29,11 +29,13 @@ import org.apache.dubbo.rpc.support.ProtocolUtils;
     
     import java.util.Map;
     
    +import static org.apache.dubbo.common.constants.CommonConstants.BROADCAST_CLUSTER;
    +import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY;
    +import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
    +import static org.apache.dubbo.rpc.Constants.LOCAL_PROTOCOL;
     import static org.apache.dubbo.rpc.Constants.SCOPE_KEY;
     import static org.apache.dubbo.rpc.Constants.SCOPE_LOCAL;
     import static org.apache.dubbo.rpc.Constants.SCOPE_REMOTE;
    -import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
    -import static org.apache.dubbo.rpc.Constants.LOCAL_PROTOCOL;
     
     /**
      * InjvmProtocol
    @@ -111,6 +113,11 @@ public class InjvmProtocol extends AbstractProtocol implements Protocol {
                 // generic invocation is not local reference
                 return false;
             } else if (getExporter(exporterMap, url) != null) {
    +            // Broadcast cluster means that multiple machines will be called,
    +            // which is not converted to injvm protocol at this time.
    +            if (BROADCAST_CLUSTER.equalsIgnoreCase(url.getParameter(CLUSTER_KEY))) {
    +                return false;
    +            }
                 // by default, go through local reference if there's the service exposed locally
                 return true;
             } else {
    diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/DemoService.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/DemoService.java
    index 8ecfaeede6..ec42830cdc 100644
    --- a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/DemoService.java
    +++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/DemoService.java
    @@ -38,4 +38,6 @@ public interface DemoService {
         int stringLength(String str);
     
         Type enumlength(Type... types);
    +
    +    String getAsyncResult();
     }
    \ No newline at end of file
    diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/DemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/DemoServiceImpl.java
    index 5725513cd9..2778d33ef9 100644
    --- a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/DemoServiceImpl.java
    +++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/DemoServiceImpl.java
    @@ -69,4 +69,15 @@ public class DemoServiceImpl implements DemoService {
         public int stringLength(String str) {
             return str.length();
         }
    +
    +    @Override
    +    public String getAsyncResult() {
    +        try {
    +            Thread.sleep(1000);
    +        } catch (InterruptedException e) {
    +            System.out.println("getAsyncResult() Interrupted");
    +        }
    +        return "DONE";
    +    }
    +
     }
    \ No newline at end of file
    diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java
    index 9371fbc512..2228aed3bb 100644
    --- a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java
    +++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java
    @@ -34,6 +34,7 @@ import java.util.List;
     import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
     import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
     import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
    +import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
     import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
     import static org.apache.dubbo.rpc.Constants.LOCAL_PROTOCOL;
     import static org.apache.dubbo.rpc.Constants.SCOPE_KEY;
    @@ -41,6 +42,7 @@ import static org.apache.dubbo.rpc.Constants.SCOPE_LOCAL;
     import static org.apache.dubbo.rpc.Constants.SCOPE_REMOTE;
     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;
     
     /**
    @@ -119,6 +121,22 @@ public class InjvmProtocolTest {
             url = URL.valueOf("fake://127.0.0.1/TestService").addParameter(GENERIC_KEY, true);
             assertFalse(InjvmProtocol.getInjvmProtocol().isInjvmRefer(url));
     
    +        url = URL.valueOf("fake://127.0.0.1/TestService").addParameter("cluster", "broadcast");
    +        assertFalse(InjvmProtocol.getInjvmProtocol().isInjvmRefer(url));
    +    }
    +
    +    @Test
    +    public void testLocalProtocolAsync() throws Exception {
    +        DemoService service = new DemoServiceImpl();
    +        URL url = URL.valueOf("injvm://127.0.0.1/TestService")
    +                .addParameter(ASYNC_KEY, true)
    +                .addParameter(INTERFACE_KEY, DemoService.class.getName()).addParameter("application", "consumer");
    +        Invoker invoker = proxy.getInvoker(service, DemoService.class, url);
    +        assertTrue(invoker.isAvailable());
    +        Exporter exporter = protocol.export(invoker);
    +        exporters.add(exporter);
    +        service = proxy.getProxy(protocol.refer(DemoService.class, url));
    +        assertNull(service.getAsyncResult());
         }
     
     }
    diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java
    index 878756d451..2021d430fc 100644
    --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java
    +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java
    @@ -58,8 +58,7 @@ public class TripleProtocol extends AbstractProtocol implements Protocol {
             String key = serviceKey(url);
             final AbstractExporter exporter = new AbstractExporter(invoker) {
                 @Override
    -            public void unexport() {
    -                super.unexport();
    +            public void afterUnExport() {
                     pathResolver.remove(url.getServiceKey());
                     pathResolver.remove(url.getServiceInterface());
                     exporterMap.remove(key);
    diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java
    index 23a77835e9..2729e6ed37 100644
    --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java
    +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java
    @@ -16,6 +16,7 @@
      */
     package org.apache.dubbo.common.serialize.hessian2;
     
    +import org.apache.dubbo.common.serialize.Cleanable;
     import org.apache.dubbo.common.serialize.ObjectInput;
     import org.apache.dubbo.common.serialize.hessian2.dubbo.Hessian2FactoryInitializer;
     
    @@ -28,7 +29,7 @@ import java.lang.reflect.Type;
     /**
      * Hessian2 object input implementation
      */
    -public class Hessian2ObjectInput implements ObjectInput {
    +public class Hessian2ObjectInput implements ObjectInput, Cleanable {
     
         private static ThreadLocal INPUT_TL = ThreadLocal.withInitial(() -> {
             Hessian2Input h2i = new Hessian2Input(null);
    @@ -109,4 +110,11 @@ public class Hessian2ObjectInput implements ObjectInput {
         public InputStream readInputStream() throws IOException {
             return mH2i.readInputStream();
         }
    +
    +    @Override
    +    public void cleanup() {
    +        if(mH2i != null) {
    +            mH2i.reset();
    +        }
    +    }
     }
    diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectOutput.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectOutput.java
    index 9844415bbd..874a2c8b21 100644
    --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectOutput.java
    +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectOutput.java
    @@ -16,6 +16,7 @@
      */
     package org.apache.dubbo.common.serialize.hessian2;
     
    +import org.apache.dubbo.common.serialize.Cleanable;
     import org.apache.dubbo.common.serialize.ObjectOutput;
     import org.apache.dubbo.common.serialize.hessian2.dubbo.Hessian2FactoryInitializer;
     
    @@ -27,7 +28,7 @@ import java.io.OutputStream;
     /**
      * Hessian2 object output implementation
      */
    -public class Hessian2ObjectOutput implements ObjectOutput {
    +public class Hessian2ObjectOutput implements ObjectOutput, Cleanable {
     
         private static ThreadLocal OUTPUT_TL = ThreadLocal.withInitial(() -> {
             Hessian2Output h2o = new Hessian2Output(null);
    @@ -106,4 +107,11 @@ public class Hessian2ObjectOutput implements ObjectOutput {
         public OutputStream getOutputStream() throws IOException {
             return mH2o.getBytesOutputStream();
         }
    +
    +    @Override
    +    public void cleanup() {
    +        if(mH2o != null) {
    +            mH2o.reset();
    +        }
    +    }
     }
    diff --git a/pom.xml b/pom.xml
    index 0384c6f899..e901145916 100644
    --- a/pom.xml
    +++ b/pom.xml
    @@ -90,12 +90,12 @@
             
             5.6.0
             3.11.1
    -        1.3
    +        2.2
             5.2.4.Final
             2.2.4
             2.2.7
             2.2
    -        2.23.4
    +        3.8.0
             
             -server -Xms256m -Xmx512m -Dfile.encoding=UTF-8
                 -Djava.net.preferIPv4Stack=true -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m
    @@ -112,16 +112,16 @@
             UTF-8
             
             3.2.0
    -        2.22.1
    +        2.22.2
             2.8.2
    -        3.6.0
    +        3.8.1
             3.2.1
    -        3.0.1
    -        9.4.11.v20180605
    -        3.0.0
    -        0.8.2
    -        1.1.0
    -        3.0.0-M2
    +        3.2.0
    +        9.4.38.v20210224
    +        3.1.2
    +        0.8.6
    +        1.2.5
    +        3.0.0-M3
             0.13
             
             true
    @@ -179,7 +179,7 @@
             
             
                 org.hamcrest
    -            hamcrest-all
    +            hamcrest
                 ${hamcrest_version}
                 test
             
    @@ -251,7 +251,7 @@
                                 
                                     com.puppycrawl.tools
                                     checkstyle
    -                                8.9
    +                                8.41
                                 
                                 
                                     org.apache.dubbo
    @@ -395,7 +395,7 @@
                         
                             org.codehaus.mojo
                             license-maven-plugin
    -                        1.20
    +                        2.0.0
                             
                                 
                                     license-check
    @@ -593,7 +593,7 @@
                 
                     org.apache.maven.plugins
                     maven-release-plugin
    -                2.5.3
    +                3.0.0-M1
                     
                         true
                         false
    @@ -639,11 +639,11 @@
                     
                     
                         maven-failsafe-plugin
    -                    2.22.1
    +                    2.22.2
                     
                     
                         maven-clean-plugin
    -                    3.0.0
    +                    3.1.0
                     
                     
                         org.ops4j.pax.exam
    @@ -652,7 +652,7 @@
                     
                     
                         maven-dependency-plugin
    -                    2.10
    +                    3.1.2
                     
                     
                         
    @@ -662,12 +662,12 @@
                     
                         org.codehaus.mojo
                         build-helper-maven-plugin
    -                    1.10
    +                    3.2.0
                     
                     
                         org.fusesource.hawtjni
                         maven-hawtjni-plugin
    -                    1.14
    +                    1.15
                     
                     
                         kr.motd.maven
    @@ -677,7 +677,7 @@
                     
                         org.apache.maven.plugins
                         maven-shade-plugin
    -                    3.2.2
    +                    3.2.4
                         
                             false